Unexpected behaviour with Viewport::setScrollOnDragEnabled(true)


#1

My app uses a Viewport to display its UI, and after calling setScrollOnDragEnabled, my UI slides up & down when I tweak knobs within the UI.

How can I ensure that the mouse events handled by the child components are not also intercepted by the Viewport scrolling?

The behaviour I’m aiming for is that the view should only scroll if you touch empty background, but not if you touch an active foreground component.

Thank you!
(using latest version of Juce master commit 75cd666a61f31eaa3ee6c6d9ad97bc404878a0d8)


#2

As far as I know there’s not a simple way to get around this. It’d be useful to have an option to enable this behaviour though so I’ll take a look at tweaking the Viewport class a bit and let you know when it’s done.

Ed


#3

That would be brilliant - thank you!


#4

I’ve added a fix for this on develop. There’s now a viewportIgnoreDragFlag member of the ComponentFlags struct in Component that you can set to indicate that a mouse drag event on it or any of its children should not move the Viewport that they are contained in if it has drag-to-scroll functionality enabled. So in your case you would do:

yourSlider.setViewportIgnoreDragFlag (true) 

and the Viewport should not move when you are dragging the slider, however it will move when you drag on the empty background or any other Components that do not have this flag set.

Ed


#5

This works in a basic use case but creates problems when using multi touch. Touching a slider with one finger and touching a non-blocking component with another creates buggy behaviour. I’d say if one finger blocks the viewport, all other fingers should be blocked, too.


#6

Tested it quickly - works for my app - but haven’t tried multi-touch yet.

So really grateful to have a fix / work-around. Thanks Ed!! :clap:

But I think this should be the default behaviour, ie: the current behaviour of intercepting touches on child components is extremely unusual - no other event model I’ve used (iOS, Mac, Windows, Motif, X11) would behave like this. As it stands this solution means I have to write special code into my child components to disable weird behaviour in some parent view controller. This is clearly back to front, imagine you were using a closed-source 3rd party component library! :smile:


#7

OK, this should work with multi-touch now.

Ed


#8

I have another question about the viewport class:
I’d like to have a rubber band effect on the edges, like on iOS. Is it possible to make the AnimatedPosition inside the viewport accessible?


#9

I don’t think it’d be very easy to do that just by accessing the position, I think it’d need the class to support it internally…


#10

I have a viewport with (homegrown) sliders inside, and noticed a (potential) lack of setScrollOnDragEnabled functionality:

When using it during a mouseDrag, it will be without effect because the DragToScrollListener’s “isViewportDragBlocked” is only set during mouseDown and mouseUp.
Adding
isViewportDragBlocked = doesMouseEventComponentBlockViewportDrag(e.eventComponent);
at the beginning of its mouseDrag function.

This is useful when if you decide about scrolling depending i.e. on the first drag distance or timestamp.

What do you think?


#11

Have you tried setting the setViewportIgnoreDragFlag flag to true in your sliders as mentioned above?


#12

Yes, that’s where I come from. Maybe I wasn’t clear…
(But… doesn’t it need to be true to block scrolling?)

I wanted to only scroll if the initial dragging speed is above a certain value. Otherwise the slider’s value is to be changed. So I call setViewportIgnoreDragFlag(true) in my mouseDrag() routine.

So here’s what I found out:

Actually it’s not the component’s viewportIgnoreDragFlag that keeps the Viewport from moving, but the DragToScrollListener::isViewportDragBlocked variable. It is set to doesMouseEventComponentBlockViewportDrag(e.eventComponent) (which sums up all components’ viewportIgnoreDragFlags) during mouse downs and ups - but not during drags, so it never became true. As a result, viewportIgnoreDragFlag can be true while the Viewport will move.

My fix works just fine and may be useful for others - but is located in juce code…:
add isViewportDragBlocked = doesMouseEventComponentBlockViewportDrag(e.eventComponent);
at the beginning of Viewport::DragToScrollListener::mouseDrag(), so isViewportDragBlocked gets set, just like it does in mouseDown() and mouseUp().


#13

Is it not possible to achieve the same behaviour by calling Viewport::setScrollOnDragEnabled() with either true or false depending on the drag speed?


#14

Well yes, that’s what you always could do, even in mouse up and down.
But then you need access to the Viewport from within the component - render this automatism useless, doesn’t it?

Other point multitouch:
When I release the component I’d need to re-enable touchScrolling, which would be bad if a second component is still “in use”

Or do I get something completely wrong? :flushed: :smile:

So just for my understanding:
why is the flag only checked in mouse up and down, but not in drag?


#15

Ah I see now. It makes sense to remove the check from mouseDown and mouseUp and only do it in mouseDrag as that’s the only place it gets used, which will also allow you to change it during a mouseDrag. I’ll add this to develop.

Also, yes I should have said

Have you tried setting the setViewportIgnoreDragFlag flag to true in your sliders as mentioned above?

in my previous post and I’ve edited it to correct that, sorry!


#16

Cool, thanks!