Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Windows] Subscribe pointer events only when needed #23515

Conversation

MartyIX
Copy link
Contributor

@MartyIX MartyIX commented Jul 9, 2024

Description of Change

The idea of the PR is that subscriptions

container.PointerEntered += OnPgrPointerEntered;
container.PointerExited += OnPgrPointerExited;
container.PointerMoved += OnPgrPointerMoved;
container.PointerPressed += OnPgrPointerPressed;
container.PointerReleased += OnPgrPointerReleased;

are only necessary if

  1. the associated element has some PointerGestureRecognizer, or
  2. these events are subscribed.

This arguments should suffice to claim that this PR is a refactoring after an element is initialization.

Now it is necessary to prove that the PR works even if

  1. Pointer gesture events change -> should be safe because of this
  2. Element's container change -> should be safe because of this

Performance impact

image

-> 98% improvement1.

Issues Fixed

Contributes to #21787

Footnotes

  1. Note that this improvement is for view where there are no PointerGestureRecognizers assigned to elements as in my use case.

@MartyIX MartyIX requested a review from a team as a code owner July 9, 2024 20:57
@MartyIX MartyIX requested review from rmarinho and PureWeen July 9, 2024 20:57
Copy link
Contributor

Hey there @MartyIX! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed.

@PureWeen
Copy link
Member

PureWeen commented Jul 10, 2024

/azp run

Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@daltzctr
Copy link
Contributor

This yields nice improvements in performance in a complex app. However, it does seem to break Visual States.

Repro:

Apply the following on an element and observe it not working.

            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="CommonStates">
                    <VisualState x:Name="PointerOver">
                        <VisualState.Setters>
                            <Setter Property="Scale" Value="1.01" />
                        </VisualState.Setters>
                    </VisualState>
                    <VisualState x:Name="Normal">
                        <VisualState.Setters>
                            <Setter Property="Scale" Value="1" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>

@MartyIX
Copy link
Contributor Author

MartyIX commented Jul 11, 2024

This yields nice improvements in performance in a complex app. However, it does seem to break Visual States.

Repro:

Apply the following on an element and observe it not working.

            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="CommonStates">
                    <VisualState x:Name="PointerOver">
                        <VisualState.Setters>
                            <Setter Property="Scale" Value="1.01" />
                        </VisualState.Setters>
                    </VisualState>
                    <VisualState x:Name="Normal">
                        <VisualState.Setters>
                            <Setter Property="Scale" Value="1" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>

Thank you for testing.

I thought that this

void ModelGestureRecognizersOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
UpdatingGestureRecognizers();
}
would take care of it. But perhaps it doesn't. I need to take a closer look.

@albyrock87
Copy link
Contributor

@MartyIX Here https://github.com/dotnet/maui/pull/23515/files#diff-779a3cbf8053213a2331cbf6a85a82af5c41cd858702c4fa4c4efdf16d7a6b13R772 use CompositeGestureRecognizers instead.

@MartyIX MartyIX force-pushed the feature/2024-07-09-Optional-pointer-event-subscription branch from 2cb2dca to 2d28e4f Compare July 11, 2024 18:11
IList<IGestureRecognizer>? gestures = view?.GestureRecognizers;

if (gestures is null)
if (_container is null || Element is not View view || view.GestureRecognizers is not IList<IGestureRecognizer> gestures)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just modernize a bit

Copy link
Contributor

@albyrock87 albyrock87 Jul 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about a simpler if (ElementGestureRecognizers is not {} gestures) return; ?
I think that view.GestureRecognizer is just wrong (not enough) in this situation.
Or do you think we would have strange side effects?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a different method. I don't want to change behavior here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps it can be done. I'm not sure. However, if I start with it - i.e. changing IList<IGestureRecognizer> to ElementGestureRecognizers - then nobody will want to review this PR and it will lead to delays.

So I sort of prefer as little change as possible.

Copy link
Contributor

@albyrock87 albyrock87 Jul 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For reference, CompositeGesureRecognizers is basically a proxy of the GestureRecognizers with the addition of the pointer over gesture recognizer which is (un)set by this method here who checks the VSM for pointer over states.

That said, and considering the only piece of code here caring about the pointer gesture recognizer is the one you changed, I feel this is a safe change.

Anyway I'll leave this to the MAUI team: my purpose is just to give more context information.

_container.PointerMoved += OnPgrPointerMoved;
_container.PointerPressed += OnPgrPointerPressed;
_container.PointerReleased += OnPgrPointerReleased;
bool hasPointerGesture = ElementGestureRecognizers.HasAnyGesturesFor<PointerGestureRecognizer>();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed according to: #23515 (comment)

@MartyIX
Copy link
Contributor Author

MartyIX commented Jul 11, 2024

I tested with https://github.com/MartyIX/maui/tree/feature/2024-07-09-Optional-pointer-event-subscription-TEST (MartyIX@62dc732)

animation

Test code

MainPage.xaml

<ContentPage
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    x:Class="Maui.Controls.Sample.MainPage"
    xmlns:local="clr-namespace:Maui.Controls.Sample">

  <VerticalStackLayout>
    <Label Text="First button has assigned a VisualStateManager with a pointer-over state."/>
    <Button x:Name="button1" Text="Button 1">
      <VisualStateManager.VisualStateGroups>
        <VisualStateGroupList>
          <VisualStateGroup>
            <VisualState x:Name="Normal" />
            <VisualState x:Name="Pressed">
              <VisualState.Setters>
                <Setter Property="BackgroundColor" Value="{AppThemeBinding Light=Yellow, Dark=Orange}" />
              </VisualState.Setters>
            </VisualState>
            <VisualState x:Name="PointerOver">
              <VisualState.Setters>
                <Setter Property="BackgroundColor" Value="Red" />
              </VisualState.Setters>
            </VisualState>
          </VisualStateGroup>
        </VisualStateGroupList>
      </VisualStateManager.VisualStateGroups>
    </Button>

    <Label Text="Second button has assigned PointerEntered and PointerExited gesture recognizers."/>
    <Button x:Name="button2" Text="Button 2">
      <Button.GestureRecognizers>
        <PointerGestureRecognizer PointerEntered="PointerGestureRecognizer_PointerEntered" PointerExited="PointerGestureRecognizer_PointerExited" />
      </Button.GestureRecognizers>
    </Button>

    <Label Text="Third button has assigned a pointer-over event in code behind."/>
    <Button x:Name="button3" Text="Button 3"/>

    <Label Text="Fourth button has assigned no pointer events."/>
    <Button x:Name="button4" Text="Button 4"/>

  </VerticalStackLayout>
</ContentPage>

MainPage.xaml.cs

namespace Maui.Controls.Sample;

public partial class MainPage : ContentPage
{
    public MainPage()
    {
  	  InitializeComponent();

  	  PointerGestureRecognizer pointerGestureRecognizer = new();
  	  pointerGestureRecognizer.PointerEnteredCommand = new Command(() =>
  	  {
  		  button3.Text = "Button 3 (pointer over)";
  		  button3.BackgroundColor = Colors.Red;
  	  });

  	  pointerGestureRecognizer.PointerExitedCommand = new Command(() =>
  	  {
  		  button3.Text = "Button 3";
  		  button3.BackgroundColor = null;
  	  });

  	  button3.GestureRecognizers.Add(pointerGestureRecognizer);
    }

    private void PointerGestureRecognizer_PointerEntered(object sender, PointerEventArgs e)
    {
  	  button2.Text = "Button 2 (pointer over)";
  	  button2.BackgroundColor = Colors.Red;
    }

    private void PointerGestureRecognizer_PointerExited(object sender, PointerEventArgs e)
    {
  	  button2.Text = "Button 2";
  	  button2.BackgroundColor = null;
    }
}

Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@MartyIX MartyIX requested a review from Foda July 13, 2024 16:51
@MartyIX MartyIX force-pushed the feature/2024-07-09-Optional-pointer-event-subscription branch from 2d28e4f to 7a4b555 Compare July 19, 2024 20:59
@jonathanpeppers
Copy link
Member

/azp run

Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@PureWeen PureWeen merged commit 6a414ea into dotnet:main Jul 21, 2024
97 checks passed
@MartyIX MartyIX deleted the feature/2024-07-09-Optional-pointer-event-subscription branch July 22, 2024 06:36
pictos pushed a commit to pictos/maui that referenced this pull request Jul 22, 2024
[Windows] Fix CollectionView DisconnectHandler SelectionMode Crash (dotnet#23726)

* Reorder events to prevent disconnect issues

* Add test

---------

Co-authored-by: Mike Corsaro <mikecorsaro@microsoft.com>

Use UIView.Window instead of the global window (dotnet#23693)

[Windows] Subscribe pointer events only when needed (dotnet#23515)

Fix iOS log exports (dotnet#23334)

* Fix ios Device Logging

* - fix up device test logging a bit more

* Update ui-tests-steps.yml

* Update ui-tests-steps.yml

* Update ios.cake

* Update ui-tests-steps.yml

* Update maui-templates.yml

* Update maui-templates.yml

remove code from oldFragment

add new shiny DialogFragment

refactoring code to find and dismiss DialogFragment

code cleanup

delete ModalContainer to use only ModalFragment

handle animation and add a map between page and dialogFragment

We've back button enabled!

After dismissing several demons summoned using obscure Android APIs, I was able to deal with the BackButtonPressed event

add modal animations as anim.xml files

using cleanup

remowork PopModalPlatformAsync to work with dialogFragment

remove tag

final adjustments on DialogFragment

change the ShowNow for Show to fix the issue

Wait for animation to complete

change local functions order

fix build

create window hooks for android (like iOS)

clean up ModalFragment fields

change Dictionary to ConditionalWeakTable

clean up event animation

refactor on Null notation

remove comments

- adjust back button

- different back button

code style

remove unused prop.

fix DontPushModalPagesWhenWindowIsDeactivated DeviceTest

completes the task

return back the way how modalManager handles android modals

normilize animation duration

Co-authored-by: Shane Neuville <shane94@hotmail.com>

remove focusability code

change how fragments are looked-up

code style
@samhouts samhouts added fixed-in-net9.0-nightly This may be available in a nightly release! fixed-in-8.0.80 labels Aug 2, 2024
@samhouts samhouts added fixed-in-net9.0-nightly This may be available in a nightly release! and removed fixed-in-net9.0-nightly This may be available in a nightly release! labels Aug 8, 2024
@github-actions github-actions bot locked and limited conversation to collaborators Sep 8, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
community ✨ Community Contribution fixed-in-8.0.80 fixed-in-net9.0-nightly This may be available in a nightly release!
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants