Bug 399324 - Scrolling changes sliders, list widgets, and numeric fields, causing unintentional changes to controls on scrollable views
Summary: Scrolling changes sliders, list widgets, and numeric fields, causing unintent...
Status: ASSIGNED
Alias: None
Product: Breeze
Classification: Unclassified
Component: QStyle (show other bugs)
Version: unspecified
Platform: unspecified Linux
: HI major
Target Milestone: ---
Assignee: Jan Blackquill
URL:
Keywords: usability
: 400350 411611 418522 445239 (view as bug list)
Depends on:
Blocks:
 
Reported: 2018-10-03 01:15 UTC by nyanpasu64
Modified: 2022-04-20 17:14 UTC (History)
12 users (show)

See Also:
Latest Commit:
Version Fixed In:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description nyanpasu64 2018-10-03 01:15:49 UTC
SUMMARY
Scrolling through a long form results in accidentally changing form values. Mouse wheel changes values of sliders, list widgets, and numeric fields, even when not focused.

STEPS TO REPRODUCE
Scroll through a page or form. If any {slider, list widget, numeric field} passes under the mouse pointer, the scroll is interrupted, and the widget's value is changed instead.

Example (Qt):
- Open KDE System Settings, Screen Edges.
- Make window vertically short.
- Scroll vertically, moving cursor so that after scrolling, cursor ends up on top of any numeric text field.

Example (Gtk+3):
- Firefox, Ctrl+P to open Print dialog
- Click Advanced tab
- Scroll down.

OBSERVED RESULT
All future scroll operations result in changing form values instead of scrolling through the form.

EXPECTED RESULT
Scrolling only affects the value of a field/widget, either:
a. if field has keyboard focus AND cursor is on top
b. never

WORKAROUND
The only way to reliably *scroll* through a form without *randomly* corrupting form state, is to position the cursor on the scrollbar before using the wheel. Which nearly defeats the purpose of the scroll wheel.

https://old.reddit.com/r/kde/comments/9cbema/mouse_wheel_changes_sliders_and_drop_down_menus/

> In e.g. Firefox this is more intelligent: If you just "scroll over" an element, it is not activated/changed. Only if you move your mouse over it by yourself and use the wheel, it triggers. I guess Firefox (or GTK?) checks on mouse over if the last input event was a scroll or a move, and only triggers on move, or if some time has passed. So you can just fly through a page without accidentally activating elements. I love this behavior.
>Long story short: Is there a way to change this to the "intelligent' behavior? If not, can I turn off the "change value via mouse wheel" feature for Qt applications completely?


SOFTWARE VERSIONS
(available in About System)
KDE Plasma Version: 5.12.6
KDE Frameworks Version: 5.44.0
Qt Version: 5.9.5

ADDITIONAL INFORMATION
Comment 1 Nate Graham 2018-10-26 20:29:13 UTC
*** Bug 400350 has been marked as a duplicate of this bug. ***
Comment 2 Nate Graham 2019-09-12 16:45:37 UTC
*** Bug 411611 has been marked as a duplicate of this bug. ***
Comment 3 Nate Graham 2020-02-20 20:07:06 UTC
Proposal: disable this behavior for controls that are located inside a scrollview when the content is scrollable, since in this case scrolling to see more content can modify the controls unintentionally. At other times, allow a scroll to modify the controls' values.
Comment 4 Patrick Silva 2020-03-13 17:10:52 UTC
*** Bug 418522 has been marked as a duplicate of this bug. ***
Comment 5 Nate Graham 2021-10-11 18:16:29 UTC Comment hidden (spam)
Comment 6 Nate Graham 2021-10-11 18:51:31 UTC
Oops, that was for the wrong bug report.

Regardless, I'm looking into implementhing nyanpasu64's suggestion. It turned out to be simple for our QtQuick controls; see https://invent.kde.org/frameworks/plasma-framework/-/merge_requests/350 and https://invent.kde.org/frameworks/qqc2-desktop-style/-/merge_requests/99.

Looks like it'll be more complicated here, as I don't see any public API to override the scroll behavior. We'll need to either override wheelEvent() for these controls in our QStyle to conditionally do nothing, or else make a Qt change to either do that or add API there that we can use downstream.
Comment 7 Mauricius 2021-10-26 22:04:09 UTC
(In reply to Nate Graham from comment #6)
> Looks like it'll be more complicated here, as I don't see any public API to
> override the scroll behavior. We'll need to either override wheelEvent() for
> these controls in our QStyle to conditionally do nothing, or else make a Qt
> change to either do that or add API there that we can use downstream.

Hi, new guy here!

After a couple hours of toying with a dummy scrollception app, I think I found a way to circumvent the problem in the classic QWidgets apps that doesn't break the old functionality but prevents accidental inputs from happening. Instead of messing with Breeze, I implemented an app wide event filter that redirects the scroll events to the widget the user would expect to react.

So this is what user sees: if the mouse is moved into a scrollable field, it should react by changing its value as normal. HOWEVER if the user keeps the mouse still and simply scrolls over the field, it will not react, but the widget that started scrolling would continue scrolling. This also applies to scroll-areas within scroll-areas: scrolling over one will not affect it, just like in browsers.

How it's done: with an event filter installed on QApp. It remembers one widget as the scroll-active one and if the receiver of an event is not scroll-active, the event will be resent to the scroll-active one. This is how it responds to events:
QMouseMoveEvent:
* Reset scroll-active to 0 when the mouse moves
* This ensures that the next scroll event will be propagated to the widget under cursor
* On the next scroll event the receiver object should be set as the scroll-active widget

QWheelEvent
* If the scroll-active widget is not scrollable anymore (for example reached the end), reset scroll-active to 0
* for example, allow scrolling parent scroll-area if the current one reached its end

* In case the receiver widget is a non-scrollable widget inside a scrollable area, find the first scrollable parent

* Change the scroll-active widget to the receiver (or its parent) only if the scroll-active variable has been reset
* i.e. prevent changing the scroll-active variable unless explicitly required

* If the receiver isn't the scroll-active widget, redirect the event to the scroll-active one


This of course isn't related to Breeze style but to Qt itself, but if we really want to apply this globally the style could perhaps force the event filter on the app it's used on.
Comment 8 Nate Graham 2021-10-28 17:22:51 UTC
Interesting ideas! Feel free to submit a merge request with your idea to https://invent.kde.org/plasma/breeze/-/merge_requests/.
Comment 9 Mauricius 2021-10-28 22:52:04 UTC
(In reply to Nate Graham from comment #8)
> Interesting ideas! Feel free to submit a merge request with your idea to
> https://invent.kde.org/plasma/breeze/-/merge_requests/.

Thanks! I still have to tweak the system a bit since it has edge cases when you reach the end of page and simultaneously land on a scrollable control, but I have a more elegant solution in mind that would handle those cases too.

The event filter is implemented in one header and one cpp file and can be installed by the app developer too, on any platform. Are you sure it would be best to implement it in Breeze? I'm new to KDE development so I wondered if there is at least some lower level KDE library that's used by all Qt apps, independent of system style. It would probably be more flexible to implement it in such place if possible.
Comment 10 Noah Davis 2021-10-28 23:03:30 UTC
(In reply to Mauricius from comment #9)
> (In reply to Nate Graham from comment #8)
> > Interesting ideas! Feel free to submit a merge request with your idea to
> > https://invent.kde.org/plasma/breeze/-/merge_requests/.
> 
> Thanks! I still have to tweak the system a bit since it has edge cases when
> you reach the end of page and simultaneously land on a scrollable control,
> but I have a more elegant solution in mind that would handle those cases too.
> 
> The event filter is implemented in one header and one cpp file and can be
> installed by the app developer too, on any platform. Are you sure it would
> be best to implement it in Breeze? I'm new to KDE development so I wondered
> if there is at least some lower level KDE library that's used by all Qt
> apps, independent of system style. It would probably be more flexible to
> implement it in such place if possible.

I suppose the Plasma Qt platform theme would be lower level than the QStyle. The repo is https://invent.kde.org/plasma/plasma-integration/
Comment 11 Nate Graham 2021-11-10 16:59:26 UTC
*** Bug 445239 has been marked as a duplicate of this bug. ***
Comment 12 David Edmundson 2021-11-30 15:18:13 UTC
Note for comboboxes it's easy, you can set QStyle::SH_ComboBox_AllowWheelScrolling to false.
If we want anything else, we should be looking at patching Qt first not introducing hacks on our side.
Comment 13 Nate Graham 2021-11-30 15:23:25 UTC
What we want here is that scrolling on controls still changes them, but if you scroll the view that a control is on, and that scroll action happens to move the cursor over a scrollable control, the control should ignore it and the view should keep on scrolling. 

Personally I find this to be a rather fiddly solution, and I think it would be simpler and more predictable to only allow scrolling on controls to change them when the controls are focused. But that would be a functional change for people used to the status quo, and a regression for controls *not* on scrollable views. So it's probably not something we can do.
Comment 14 Mauricius 2022-04-20 16:53:37 UTC
(In reply to Noah Davis from comment #10)
> (In reply to Mauricius from comment #9)
> > (In reply to Nate Graham from comment #8)
> > > Interesting ideas! Feel free to submit a merge request with your idea to
> > > https://invent.kde.org/plasma/breeze/-/merge_requests/.
> > 
> > Thanks! I still have to tweak the system a bit since it has edge cases when
> > you reach the end of page and simultaneously land on a scrollable control,
> > but I have a more elegant solution in mind that would handle those cases too.
> > 
> > The event filter is implemented in one header and one cpp file and can be
> > installed by the app developer too, on any platform. Are you sure it would
> > be best to implement it in Breeze? I'm new to KDE development so I wondered
> > if there is at least some lower level KDE library that's used by all Qt
> > apps, independent of system style. It would probably be more flexible to
> > implement it in such place if possible.
> 
> I suppose the Plasma Qt platform theme would be lower level than the QStyle.
> The repo is https://invent.kde.org/plasma/plasma-integration/

Hi,
I haven't updated anything regarding this bug in a long time, but I found some free time and after struggling with debugging the KDEPlasmaPlatformTheme library I confirmed that the method works. I don't know if this hack is the best way of solving this and whether there has been some work on the Qt side, but for now this simple patch seems to improve usability system-wide. 
I don't know what's the best way to test the code thoroughly except by replacing the library on runtime, which often crashes the DE (since I'm a n00b it seems), so some more testing might be required.
I submitted a merge request https://invent.kde.org/plasma/plasma-integration/-/merge_requests/44

Btw, should I take the assignee position for the issue? I haven't seen any more work being done on this by others
Comment 15 Nate Graham 2022-04-20 16:55:19 UTC
In progress with https://invent.kde.org/plasma/plasma-integration/-/merge_requests/44!
Comment 16 Nate Graham 2022-04-20 17:14:21 UTC
> Btw, should I take the assignee position for the issue? I haven't seen any more work being done on this by others
Don't worry about it. :)