microsoft / WindowsCompositionSamples

The Windows Composition Samples have moved here: https://github.com/microsoft/WindowsAppSDK-Samples/tree/main/Samples/SceneGraph
https://github.com/microsoft/WindowsAppSDK-Samples/tree/main/Samples/SceneGraph
MIT License
1.12k stars 287 forks source link

Detect two finger gestures over ScrollViewer #118

Closed mjracca closed 5 years ago

mjracca commented 8 years ago

The question

We need to detect two finger gestures (pan and pinch) over a ScrollViewer, is there any way to do this?

The context

We have multiple issues when trying to pan and zoom a ScrollViewer which contains multiple elements. This elements can be dragged as well. We don't want to select the elements first to move them around as others apps, since we value the speed it give us like this.

Here is an normal example of our canvas (it can be much more dense than this): image

Errors cases

I have drawn 4 examples of what we want to achieve, and what currently happens, as an example. The yellow square is a XAML element that is inside a ScrollViewer, and the dots are the fingers and their movement.

1. Moving an element and panning the canvas when trying to zoom

What we do: We try to pinch the canvas and accidentally touch one element with one finger, and the canvas with the other. What we expect: To zoom the canvas. What happens: The element moves with one finger, the canvas pan with the other.

2. Moving an element when trying to pan

What we do: We try to pan the canvas with two fingers and accidentally touch one element with one finger, and the canvas with the other. What we expect: To pan the canvas. What happens: The element moves with one finger, the canvas pan with the other.

3. Moving two elements when trying to zoom

What we do: We try to pinch the canvas and accidentally touch one element with one finger, and another with the other finger. What we expect: To zoom the canvas. What happens: Both elements move with each finger.

4. Moving two elements at the same time

What we do: We try moving two elements at the same time with two different hands. What we expect: Both elements move with each finger. What happens: Both elements move with each finger. 👍

We don't want to break this interaction, if the fingers are too far apart, if should be registered as individual pointers and not as a two finger gesture.

It would be really helpful if we could detect two finger gestures and use it to pan and zoom the canvas, instead of moving elements.

pgills commented 8 years ago

@mjracca Thanks for the detailed question. I'm going to ask @likuba to see if there is possibly something that can help with your specific scenario. It may take a while as it may touch other parts of the system besides Windows.UI.Composition.

mjracca commented 8 years ago

Thanks!

Testing around, our main issue is that the ScrollViewer intercepts all the pointers events, so we can't even make our custom solution. The only way would be to intercept every pointer and basically implement the ScrollViewer from scratch. Of course, we don't want this.

An alternative solution could be if somehow we could intercept the pointers, but then "transfer" them to the ScrollViewer when we decide it should scroll or pan, or to another XAML element. That could also help, but we couldn't find anything like it.

Any tip or alternative solution would be really helpful!

mikehenderlight commented 8 years ago

Hi @mjracca ,

So I assume you are trying to hook the PointerPressed event on ScrollViewer and you are not seeing the event fire? If that is true, then it usually means that your ManipulationMode property on the ScrollViewer is set to something other than System and therefore the events are not being routed to the ScrollViewer. Can you check the value for ManipulationMode?

mjracca commented 8 years ago

@mikehenderlight yes, we have ScrollViewer's ManipulationMode set to System. PointerPressed isn't the issue, but PointerMoved and PointerExited. This events are not being fired when you are zooming/panning the ScrollViewer (it's easier to see this when using touch input).

mikehenderlight commented 8 years ago

Hmm, that should work. Did you try to build a small repro by just using a ScrollViewer that contains and empty InkCanvas? When I did that, PointerMoved fired fine for me. It may help to tease out the problem.

mjracca commented 8 years ago

Sure! Here is a small repro: ScrollViewerEvents.zip

I used a Canvas instead of an InkCanvas (it doesn't work either) because this is how we use it.

mjracca commented 8 years ago

News?

mikehenderlight commented 8 years ago

Sorry, I was out a couple of days last week. So when you run your repro app, are you not getting the pointer events raised at all? When I run your app, I see PointerPressed and PointerMoved, etc. Granted, I am running a newer build than you are.

mjracca commented 8 years ago

We are testing on the latest Fast ring build, and we are not receiving the PointerMoved and PointerRelease

nopointermove

JustinXinLiu commented 8 years ago

@mikehenderlight unless the behavior is changed in your build, PointerMoved and PointerReleased events won't be raised within a ScrollViewer.

PointerPressed gets raised properly because it happens prior to the scrolling. As soon as the ScrollViewer starts to scroll, all events get eaten by it.

mjracca commented 8 years ago

Yes! That's exactly what happens.

Any way to make it work, at least low level? We need more control over the pointers.

JustinXinLiu commented 8 years ago

I tried calling ScrollViewer.CancelDirectManipulations() to take back control when there's more than one fingers pressed, but this only works if I put down two fingers at the exact same time. If I press with one finger first, the ScrollViewer will start scrolling so the second finger press won't be raised.

I too would love to know if there's any solution to this...

tomzorz commented 8 years ago

Did you try adding the events with the this.AddHandler(PointerMoved, new PointerEventHandler(pointerMovedHandler), true); syntax?

mjracca commented 8 years ago

Yes, still the same, no event

mikehenderlight commented 8 years ago

Can you try the combination of CancelDirectManipulations() and TryStartDirectManipuation()?

    private void PrintTouchEvent(PointerRoutedEventArgs e, string eventName)
    {
        scrollviewer.CancelDirectManipulations();
        TryStartDirectManipulation(e.Pointer);
        if (e.Pointer.PointerDeviceType == PointerDeviceType.Touch)
            System.Diagnostics.Debug.WriteLine(eventName);
    }
mjracca commented 8 years ago

scrollviewer.CancelDirectManipulations() will only block the ScrollViewer (and raise PointerMoved) if using two fingers at the same time, as @JustinXinLiu said. If you only use one finger, the ScrollViewer will pan normally, and won't raise PointerMoved. If you touch with one finger and move a little before touching with the other finger, then the ScrollViewer will pan and zoom normally.

Taking those limitations into account, we can't use this to solve our issue.

mjracca commented 8 years ago

Ok, so I figure out what was happening. It appears that PointerPressed was being called before DirectManipulationStarted. If I CancelDirectManipulations when the event DirectManipulationStarted raises, then I almost can block the ScrollViewer all the time.

I will try some experiments to see if this could work and write you back. Thanks!

mjracca commented 8 years ago

Testing a little further, we are not being able to resume scrolling once canceled.

pointer entered - id: 1306 pointer pressed - id: 1306 DirectManipulationStarted => CancelDirectManipulations: True pointer entered - id: 1306 pointer moved - id: 1306 => TryStartDirectManipulation: False

megamingus commented 8 years ago

how can we make TryStartDirectMaipulation to work ?? :dizzy_face:

mjracca commented 8 years ago

News? How could we make this work?

mikehenderlight commented 8 years ago

I am looping in someone else that may be able to help...Stay tuned.

decademoon commented 8 years ago

The ScrollViewer control has to be the most mysterious control in existence 😛. I really wish every low-level detail of exactly how it works was documented on MSDN.

olkham commented 6 years ago

Over a year later and this is still open? Did anyone solve this?

mjracca commented 6 years ago

Not really 😞

micahl commented 6 years ago

I made some slight changes to your simple repro app.

I believe this will allow you to intercept the events to figure out whether you need to handle it or pass it on to the system to handle the panning/zooming. Give it a try and let me know if it addresses your issue.

ScrollViewerEvents.zip

likuba commented 5 years ago

Closing this one out due to inactivity--please re-open if the solution posted above is insufficient.

ejhg commented 3 years ago

So glad @micahl 's workaround works. I've spent countless hours trying different workarounds.

It's 2021 and CancelDirectManipulations() still doesn't work within CancelDirectManipulations() as one may reasonably expect. I'm using Microsoft.UI.Xaml 2.6-prerelease.

ejhg commented 1 year ago

It's 2023, and I somehow ended up in this thread. I skipped to the very last comment, and it somewhat resonated with my experience. Then I realized I myself wrote it in 2021. Jeez, hello from the future. I'm sure I'll be here again in a few years.