consume every scroll event in a scrollPanel

Started by Nafffen, 30 December 2022, 19:01:14

Nafffen

Hello,

What is the best way to have a scrollPanel not dispatch the scroll event to parent when the scrollbar is at the tip ? Did I even understand well the behavior ?
Is making a custom widget subclass of scrollPanel a good idea ? with mouseWheelScrolled override

Thank you

texus

Creating a custom widget that inherits from ScrollablePanel and overrides the mouseWheelScrolled would be an option, but I feel like this is a behavior that can still be improved in TGUI itself (e.g. by adding a boolean flag to the widget to allow you to choose the behavior).

The behavior of passing the scroll event to the parent is actually rather new, it was only added 4 months ago and I knew someone was going to ask for a different behavior at some point (and I decided to wait with "fixing" this issue until someone actually needed a fix).

There are two behaviors that are easy to implement, and I like neither of them:
- Absorb all scroll events. You won't be able to scroll the outer scrollbar while the mouse is on top of the scrollable panel. This is what you are asking and this is the old behavior. I've worked with interfaces that work like that and I really disliked it: you can get into situations where you are scrolling the outer panel and suddenly the scrolling stops because your mouse is now on top of another panel and you have to move the mouse to continue scrolling.
- Pass event to parent when the panel can't scroll any further. This is the current behavior. The big downside is that you practically always end up scrolling too far and end up scrolling the outer panel while you just wanted to go to the end of the inner panel.

I've looked at how this is handled in other applications when I changed the code 4 months ago. What is typically done, and what I consider the correct/best behavior, is too not pass any events to the parent for a short time after scrolling. When the scrollbar reaches the end of the inner panel and you keep scrolling, nothing happens. But if you stop for a second and then start scrolling, the outer panel will scroll even when the mouse is still located on top of the inner panel. This is unfortunately harder to implement.

Which brings me to my question: do you really want to fully disable scrolling of the outer panel while the mouse is on top of the inner panel? Or would it also work for your use case if the events where only suppressed for a short time after scrolling the inner panel?

Nafffen

Thank you for the clarification, I understand why this is not the first behavior anymore.

In my case, the scroll event passed to the parent is actually not consumed by any widget, it is my game event handler which comsumes it, so when the scroll bar comes to the tip, my game is zooming or dezooming the view, and I absolutely don't want this behavior. Then I think I would be happy with the old first behavior, as I would not deal with the main drawback you mentioned (because I don't have two nested scroll bars).

I understand this is not the best behavior for all of us, the flag to allow us to choose is a good idea in my opinion

Thank you again for your work on TGUI

texus

#3
I never thought about that case before, for that use case the old behavior indeed worked a lot better.

I've looked at the behavior in Firefox again on both Windows and Linux and this is what I intend to implement for nested scrolling now:
- When you start scrolling, a panel remembers which widget is getting the scroll events and "locks" it.
- Any scroll events that occur while a widget is locked will be passed to the locked widget instead of the widget below the mouse.
- If no scrollbar has moved for one second, the scrollbars are unlocked. (The time seems much longer than 1s in Firefox, but I'm going to start with just one second in TGUI and determine if it needs to be changed later)
- If a mouse move event is received, the scrollbars are unlocked.

While that would fix nested scrollbars, it might not yet solve your situation. For your case there are several options:
1) Do nothing. Right now the behavior is a big problem for you because the view changes immediately when the scrollbar reaches the tip. If I implement the locking, the scroll event will be absorbed for a second (and handleEvent will still return true for scroll events that happen immediately after the scrollbar reached the tip). If the scrollbar is already at the tip, and the user then moves its mouse on top of the panel and starts scrolling, is it really an issue if it zooms your view? If so, then I will need to implement one of the other options.
2) Make handleEvent always return true for mouse wheel events when the mouse is on top of a TGUI widget. This is probably easy to implement and probably works well for most people. But it would also absorb scroll events when the mouse is e.g. on top of a button or a non-scrollable panel. If this becomes a problem for other people then I could add a boolean in the gui to decide on what behavior to use.
3) Adding a boolean to the ScrollablePanel to make it absorb all scroll events. I don't like this option simply because ScrollablePanel isn't the only widget that can handle scroll events, I would need to do the same for widgets like Label and TextArea as well.

So if you need the behavior then I will implement the second option, otherwise I will go with the first option and only fix the scrolling that occurs directly after the scrollbar reaches the tip.

Nafffen

Quote1) ... If the scrollbar is already at the tip, and the user then moves its mouse on top of the panel and starts scrolling, is it really an issue if it zooms your view?
Yes I don't want this behavior

Quote2) ... But it would also absorb scroll events when the mouse is e.g. on top of a button or a non-scrollable panel.
In my case it will not be a problem but I understand it can be for other users

texus

In the latest TGUI 0.10-dev version the handleEvent function will now return true when scrolling while on top of a widget.

Nafffen