unoplatform / uno

Open-source platform for building cross-platform native Mobile, Web, Desktop and Embedded apps quickly. Create rich, C#/XAML, single-codebase apps from any IDE. Hot Reload included! 90m+ NuGet Downloads!!
https://platform.uno
Apache License 2.0
8.89k stars 720 forks source link

Pointer handling inconsistency #3165

Open MartinZikmund opened 4 years ago

MartinZikmund commented 4 years ago

Current behavior

This issue is very specific but affects the functionality of SplitButton control in Uno (#2885).

Suppose the following XAML:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <Button
    x:Name="LeftButton"
    Grid.Column="0"
    HorizontalAlignment="Stretch"
    VerticalAlignment="Stretch">
        Left Button
    </Button>
    <Button
    x:Name="RightButton"
    Grid.Column="1"
    HorizontalAlignment="Stretch"
    VerticalAlignment="Stretch">
        Right Button
    </Button>
</Grid>

Two stretched buttons in two grid columns.

The code-behind is as follows:

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();

        AttachEvents(LeftButton);
        AttachEvents(RightButton);
    }

    private void AttachEvents(Button button)
    {
        button.Click += OnClick;
        button.PointerEntered += OnPointerEntered;
        button.PointerExited += OnPointerExited;
        button.PointerPressed += OnPointerPressed;
        button.PointerReleased += OnPointerReleased;
        button.PointerCanceled += OnPointerCanceled;
        button.PointerCaptureLost += OnPointerCaptureLost;
    }

    private void OnClick(object sender, RoutedEventArgs e)
    {
        System.Diagnostics.Debug.WriteLine(((Button)sender).Name + " was clicked");
    }

    private void OnPointerEntered(object sender, PointerRoutedEventArgs p)
    {
        System.Diagnostics.Debug.WriteLine("Entered");
        OnPointerEvent();
    }

    private void OnPointerExited(object sender, PointerRoutedEventArgs p)
    {
        System.Diagnostics.Debug.WriteLine("Exited");
        OnPointerEvent();
    }

    private void OnPointerMoved(object sender, PointerRoutedEventArgs p)
    {
        System.Diagnostics.Debug.WriteLine("Moved");
        OnPointerEvent();
    }

    private void OnPointerReleased(object sender, PointerRoutedEventArgs p)
    {
        System.Diagnostics.Debug.WriteLine("Released");
        OnPointerEvent();
    }

    private void OnPointerPressed(object sender, PointerRoutedEventArgs p)
    {
        System.Diagnostics.Debug.WriteLine("Pressed");
        OnPointerEvent();
    }

    private void OnPointerCanceled(object sender, PointerRoutedEventArgs p)
    {
        System.Diagnostics.Debug.WriteLine("Canceled");
        OnPointerEvent();
    }

    private void OnPointerCaptureLost(object sender, PointerRoutedEventArgs p)
    {
        System.Diagnostics.Debug.WriteLine("CaptureLost");
        OnPointerEvent();
    }

    private void OnPointerEvent()
    {            
        RightButton.SetValue(Grid.ColumnProperty, 0);
        RightButton.SetValue(Grid.ColumnSpanProperty, 2);
    }
}

On any pointer event, the right button is spanned to cover both columns of the grid. Observe that under Uno, for example on Android and iOS, when the left button area is tapped, Pointer Entered event is called (which makes the right button span), but after this left button's Click event is called.

Note this affects only touch. Mouse and pen will trigger pointer moved before user is able to click, so the button will already be spanned. So macOS is the only one safe here :-D .

Expected behavior

On UWP for tap, Pointer entered event is called, which makes the right button span and right button's Click event is called.

How to reproduce it (as minimally and precisely as possible)

See above.

Environment

Nuget Package: latest

Package Version(s): dev

Affected platform(s):

Visual Studio:

Relevant plugins:

Anything else we need to know?

jeromelaban commented 4 years ago

@dr1rrb is this an existing issue ?

dr1rrb commented 4 years ago

Hum I don't think that we already have a an issue for that.

I've tested that updating the template (visual states) while pointer is pressed would result in acceptable pointers events sequence, but moving elements over another element while pointer is pressed is kind of things I've classified as "stretch case" and didn't validated it.

dr1rrb commented 4 years ago

Thinking about it, it's totally makes sense: When we press the pointer, the Button captures the pointer and it will then receive all pointer events until release. On UWP, they are probably validating that the element is not covered when the pointer is released ... but I'm not sure that we are able to validate that with uno yet (it would requires an API to get all elements at a given coordinate)

MartinZikmund commented 6 months ago

Tested now that this is still a problem, even on Skia