Skip to content

Commit

Permalink
[WinUI] Allocate less when updating gestures (#21450)
Browse files Browse the repository at this point in the history
* `EnumerableExtensions`: Enable nullable

* Add `EnumerableExtensions.HasAnyGesturesFor`

* Sandbox measure code

* Revert "Sandbox measure code"

This reverts commit e984695.

* Do not create lambdas + apply MSVS suggestion regarding pattern matching + add braces

* Make `HasAnyGesturesFor` `internal`

* Reimplement by adding `FirstGestureOrDefault`
  • Loading branch information
MartyIX authored Apr 6, 2024
1 parent d743f9f commit 0c02e4b
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 36 deletions.
68 changes: 45 additions & 23 deletions src/Controls/src/Core/EnumerableExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,64 +1,86 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.ComponentModel;

namespace Microsoft.Maui.Controls.Internals
{
static class EnumerableExtensions
{
public static bool HasChildGesturesFor<T>(this IEnumerable<GestureElement> elements, Func<T, bool> predicate = null) where T : GestureRecognizer
public static bool HasChildGesturesFor<T>(this IEnumerable<GestureElement>? elements, Func<T, bool>? predicate = null) where T : GestureRecognizer
{
if (elements == null)
if (elements is null)
{
return false;

if (predicate == null)
predicate = x => true;
}

foreach (var element in elements)
{
foreach (var item in element.GestureRecognizers)
{
var gesture = item as T;
if (gesture != null && predicate(gesture))
if (item is T gesture && (predicate is null || predicate(gesture)))
{
return true;
}
}
}

return false;
}

public static IEnumerable<T> GetChildGesturesFor<T>(this IEnumerable<GestureElement> elements, Func<T, bool> predicate = null) where T : GestureRecognizer
public static IEnumerable<T> GetChildGesturesFor<T>(this IEnumerable<GestureElement>? elements, Func<T, bool>? predicate = null) where T : GestureRecognizer
{
if (elements == null)
if (elements is null)
{
yield break;

if (predicate == null)
predicate = x => true;
}

foreach (var element in elements)
{
foreach (var item in element.GestureRecognizers)
{
var gesture = item as T;
if (gesture != null && predicate(gesture))
if (item is T gesture && (predicate is null || predicate(gesture)))
{
yield return gesture;
}
}
}
}

public static IEnumerable<T> GetGesturesFor<T>(this IEnumerable<IGestureRecognizer> gestures, Func<T, bool> predicate = null) where T : GestureRecognizer
/// <remarks>The method makes a defensive copy of the gestures.</remarks>
public static IEnumerable<T> GetGesturesFor<T>(this IEnumerable<IGestureRecognizer>? gestures, Func<T, bool>? predicate = null) where T : GestureRecognizer
{
if (gestures == null)
if (gestures is null)
{
yield break;

if (predicate == null)
predicate = x => true;
}

foreach (IGestureRecognizer item in new List<IGestureRecognizer>(gestures))
{
var gesture = item as T;
if (gesture != null && predicate(gesture))
if (item is T gesture && (predicate is null || predicate(gesture)))
{
yield return gesture;
}
}
}

internal static bool HasAnyGesturesFor<T>(this IEnumerable<IGestureRecognizer>? gestures, Func<T, bool>? predicate = null) where T : GestureRecognizer
=> FirstGestureOrDefault(gestures, predicate) is not null;

internal static T? FirstGestureOrDefault<T>(this IEnumerable<IGestureRecognizer>? gestures, Func<T, bool>? predicate = null) where T : GestureRecognizer
{
if (gestures is null)
{
return null;
}

foreach (IGestureRecognizer item in gestures)
{
if (item is T gesture && (predicate is null || predicate(gesture)))
{
return gesture;
}
}

return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -670,28 +670,32 @@ void PinchComplete(bool success)

void UpdateDragAndDropGestureRecognizers()
{
if (_container == null)
if (_container is null)
{
return;
}

var view = Element as View;
IList<IGestureRecognizer>? gestures = view?.GestureRecognizers;

if (gestures == null)
if (gestures is null)
{
return;
}

_container.CanDrag = gestures.GetGesturesFor<DragGestureRecognizer>()
.FirstOrDefault()?.CanDrag ?? false;
bool canDrag = gestures.FirstGestureOrDefault<DragGestureRecognizer>()?.CanDrag ?? false;
_container.CanDrag = canDrag;

_container.AllowDrop = gestures.GetGesturesFor<DropGestureRecognizer>()
.FirstOrDefault()?.AllowDrop ?? false;
bool allowDrop = gestures.FirstGestureOrDefault<DropGestureRecognizer>()?.AllowDrop ?? false;
_container.AllowDrop = allowDrop;

if (_container.CanDrag)
if (canDrag)
{
_container.DragStarting += HandleDragStarting;
_container.DropCompleted += HandleDropCompleted;
}

if (_container.AllowDrop)
if (allowDrop)
{
_container.DragOver += HandleDragOver;
_container.Drop += HandleDrop;
Expand All @@ -714,7 +718,7 @@ void UpdatingGestureRecognizers()
IList<TapGestureRecognizer>? childGestures =
children?.GetChildGesturesFor<TapGestureRecognizer>().ToList();

if (gestures.GetGesturesFor<TapGestureRecognizer>(g => g.NumberOfTapsRequired == 1).Any()
if (gestures.HasAnyGesturesFor<TapGestureRecognizer>(g => g.NumberOfTapsRequired == 1)
|| children?.GetChildGesturesFor<TapGestureRecognizer>(g => g.NumberOfTapsRequired == 1).Any() == true)
{
_container.Tapped += OnTap;
Expand All @@ -728,7 +732,7 @@ void UpdatingGestureRecognizers()
}
}

if (gestures.GetGesturesFor<TapGestureRecognizer>(g => g.NumberOfTapsRequired == 1 || g.NumberOfTapsRequired == 2).Any()
if (gestures.HasAnyGesturesFor<TapGestureRecognizer>(g => g.NumberOfTapsRequired == 1 || g.NumberOfTapsRequired == 2)
|| children?.GetChildGesturesFor<TapGestureRecognizer>(g => g.NumberOfTapsRequired == 1 || g.NumberOfTapsRequired == 2).Any() == true)
{
_container.DoubleTapped += OnTap;
Expand All @@ -747,11 +751,13 @@ void UpdatingGestureRecognizers()
_container.PointerPressed += OnPgrPointerPressed;
_container.PointerReleased += OnPgrPointerReleased;

bool hasSwipeGesture = gestures.GetGesturesFor<SwipeGestureRecognizer>().GetEnumerator().MoveNext();
bool hasPinchGesture = gestures.GetGesturesFor<PinchGestureRecognizer>().GetEnumerator().MoveNext();
bool hasPanGesture = gestures.GetGesturesFor<PanGestureRecognizer>().GetEnumerator().MoveNext();
bool hasSwipeGesture = gestures.HasAnyGesturesFor<SwipeGestureRecognizer>();
bool hasPinchGesture = gestures.HasAnyGesturesFor<PinchGestureRecognizer>();
bool hasPanGesture = gestures.HasAnyGesturesFor<PanGestureRecognizer>();
if (!hasSwipeGesture && !hasPinchGesture && !hasPanGesture)
{
return;
}

//We can't handle ManipulationMode.Scale and System , so we don't support pinch/pan on a scrollview
if (Element is ScrollView)
Expand Down

0 comments on commit 0c02e4b

Please sign in to comment.