-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
[WinUI] Allocate less when updating gestures #21450
Changes from 4 commits
acabea4
19d7b69
e984695
1b3612b
39b7ec6
edd66ef
acadf48
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,16 @@ | ||
#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) | ||
if (predicate is null) | ||
predicate = x => true; | ||
|
||
foreach (var element in elements) | ||
|
@@ -26,12 +24,12 @@ public static bool HasChildGesturesFor<T>(this IEnumerable<GestureElement> eleme | |
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) | ||
if (predicate is null) | ||
predicate = x => true; | ||
|
||
foreach (var element in elements) | ||
|
@@ -43,12 +41,13 @@ public static IEnumerable<T> GetChildGesturesFor<T>(this IEnumerable<GestureElem | |
} | ||
} | ||
|
||
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) | ||
if (predicate is null) | ||
predicate = x => true; | ||
|
||
foreach (IGestureRecognizer item in new List<IGestureRecognizer>(gestures)) | ||
|
@@ -60,5 +59,25 @@ public static IEnumerable<T> GetGesturesFor<T>(this IEnumerable<IGestureRecogniz | |
} | ||
} | ||
} | ||
|
||
public static bool HasAnyGesturesFor<T>(this IEnumerable<IGestureRecognizer>? gestures, Func<T, bool>? predicate = null) where T : GestureRecognizer | ||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we make the new method(s) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in edd66ef. |
||
if (gestures is null) | ||
{ | ||
return false; | ||
} | ||
|
||
predicate ??= x => true; | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we just do a null check inside the if (item is T gesture && (predicate is null || predicate(gesture)))
{
return true;
} This way we aren't creating a lambda like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in 39b7ec6. |
||
foreach (IGestureRecognizer item in gestures) | ||
{ | ||
if (item is T gesture && predicate(gesture)) | ||
{ | ||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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.GetGesturesFor<DragGestureRecognizer>().FirstOrDefault()?.CanDrag ?? false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Store value in a local variable, so we save 1 interop call on L692 (i.e. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this use your new There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have come up with acadf48. Not really sure if that is what you had in mind. |
||
_container.CanDrag = canDrag; | ||
|
||
_container.AllowDrop = gestures.GetGesturesFor<DropGestureRecognizer>() | ||
.FirstOrDefault()?.AllowDrop ?? false; | ||
bool allowDrop = gestures.GetGesturesFor<DropGestureRecognizer>().FirstOrDefault()?.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; | ||
|
@@ -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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should save 1 allocation. |
||
|| children?.GetChildGesturesFor<TapGestureRecognizer>(g => g.NumberOfTapsRequired == 1).Any() == true) | ||
{ | ||
_container.Tapped += OnTap; | ||
|
@@ -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; | ||
|
@@ -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>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 3 allocations here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So one thing to consider is this would still iterate over the gestures list three times. How many are normally in this list? If it's usually like 1-5, that might be OK. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the list contains only a handful of items as you said. |
||
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) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is to avoid allocation on L53.