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

[MacCatalyst] Implement PointerPressed and PointerReleased #16925

Merged
merged 12 commits into from Nov 27, 2023
Expand Up @@ -20,7 +20,7 @@ class GesturePlatformManager : IDisposable
{
readonly NotifyCollectionChangedEventHandler _collectionChangedHandler;

readonly Dictionary<IGestureRecognizer, UIGestureRecognizer> _gestureRecognizers = new Dictionary<IGestureRecognizer, UIGestureRecognizer>();
readonly Dictionary<IGestureRecognizer, List<UIGestureRecognizer?>> _gestureRecognizers = new Dictionary<IGestureRecognizer, List<UIGestureRecognizer?>>();
readonly List<INativeObject> _interactions = new List<INativeObject>();
readonly IPlatformViewHandler _handler;

Expand Down Expand Up @@ -88,10 +88,16 @@ public void Dispose()
tapGestureRecognizer.PropertyChanged -= OnTapGestureRecognizerPropertyChanged;
}

if (PlatformView != null)
PlatformView.RemoveGestureRecognizer(kvp.Value);
kvp.Value.ShouldReceiveTouch = null;
kvp.Value.Dispose();
foreach(var uiGestureRecognizer in kvp.Value)
{
if (uiGestureRecognizer is null)
continue;

if (PlatformView != null)
PlatformView.RemoveGestureRecognizer(uiGestureRecognizer);
uiGestureRecognizer.ShouldReceiveTouch = null;
uiGestureRecognizer.Dispose();
}
}

if (PlatformView != null && OperatingSystem.IsIOSVersionAtLeast(11))
Expand Down Expand Up @@ -221,53 +227,27 @@ public void Dispose()

}

protected virtual UIGestureRecognizer? GetPlatformRecognizer(IGestureRecognizer recognizer)
protected virtual List<UIGestureRecognizer?>? GetPlatformRecognizer(IGestureRecognizer recognizer)
{
if (recognizer == null)
return null;

var weakRecognizer = new WeakReference(recognizer);
var weakEventTracker = new WeakReference(this);


var tapGestureRecognizer = CreateTapRecognizer(weakEventTracker, weakRecognizer);

if (tapGestureRecognizer != null)
{
return tapGestureRecognizer;
return new List<UIGestureRecognizer?> { tapGestureRecognizer };
}

var pointerGestureRecognizer = recognizer as PointerGestureRecognizer;

if (pointerGestureRecognizer != null && OperatingSystem.IsIOSVersionAtLeast(13))
{
var uiRecognizer = CreatePointerRecognizer(r =>
{
if (weakRecognizer.Target is PointerGestureRecognizer pointerGestureRecognizer &&
weakEventTracker.Target is GesturePlatformManager eventTracker &&
eventTracker._handler?.VirtualView is View view &&
eventTracker._handler?.MauiContext?.GetPlatformWindow() is UIWindow window)
{
var originPoint = r.LocationInView(eventTracker?.PlatformView);
var platformPointerArgs = new PlatformPointerEventArgs(r.View, r);

switch (r.State)
{
case UIGestureRecognizerState.Began:
pointerGestureRecognizer.SendPointerEntered(view, (relativeTo) => CalculatePosition(relativeTo, originPoint, weakRecognizer, weakEventTracker), platformPointerArgs);
break;
case UIGestureRecognizerState.Changed:
pointerGestureRecognizer.SendPointerMoved(view, (relativeTo) => CalculatePosition(relativeTo, originPoint, weakRecognizer, weakEventTracker), platformPointerArgs);
break;
case UIGestureRecognizerState.Cancelled:
case UIGestureRecognizerState.Failed:
case UIGestureRecognizerState.Ended:
pointerGestureRecognizer.SendPointerExited(view, (relativeTo) => CalculatePosition(relativeTo, originPoint, weakRecognizer, weakEventTracker), platformPointerArgs);
break;
}
}
});
return uiRecognizer;
var uiRecognizers = CreatePointerRecognizer(weakRecognizer, weakEventTracker);
return uiRecognizers;
}

var swipeRecognizer = recognizer as SwipeGestureRecognizer;
Expand All @@ -283,7 +263,7 @@ public void Dispose()
swipeGestureRecognizer.SendSwiped(view, direction);
});
var uiRecognizer = CreateSwipeRecognizer(swipeRecognizer.Direction, returnAction, 1);
return uiRecognizer;
return new List<UIGestureRecognizer?> { uiRecognizer };
}

var pinchRecognizer = recognizer as IPinchGestureController;
Expand Down Expand Up @@ -343,7 +323,7 @@ public void Dispose()
}
}
});
return uiRecognizer;
return new List<UIGestureRecognizer?> { uiRecognizer };
}

var panRecognizer = recognizer as PanGestureRecognizer;
Expand Down Expand Up @@ -390,7 +370,7 @@ public void Dispose()
}
}
});
return uiRecognizer;
return new List<UIGestureRecognizer?> { uiRecognizer };
}

return null;
Expand Down Expand Up @@ -424,9 +404,70 @@ UISwipeGestureRecognizer CreateSwipeRecognizer(SwipeDirection direction, Action<

[SupportedOSPlatform("ios13.0")]
[SupportedOSPlatform("maccatalyst13.0")]
CustomHoverGestureRecognizer CreatePointerRecognizer(Action<UIHoverGestureRecognizer> action)
List<UIGestureRecognizer?> CreatePointerRecognizer(WeakReference weakRecognizer, WeakReference weakEventTracker)
{
var result = new CustomHoverGestureRecognizer(action);
bool exited = false;

Action<UIGestureRecognizer> action = (pointerGesture) =>
{
if (weakRecognizer.Target is PointerGestureRecognizer pointerGestureRecognizer &&
weakEventTracker.Target is GesturePlatformManager eventTracker &&
eventTracker._handler?.VirtualView is View view &&
eventTracker._handler?.MauiContext?.GetPlatformWindow() is UIWindow window)
{
var originPoint = pointerGesture.LocationInView(eventTracker?.PlatformView);
var platformPointerArgs = new PlatformPointerEventArgs(pointerGesture.View, pointerGesture);

switch (pointerGesture.State)
{
case UIGestureRecognizerState.Began:
exited = false;
if (pointerGesture is UIHoverGestureRecognizer)
pointerGestureRecognizer.SendPointerEntered(view, (relativeTo) => CalculatePosition(relativeTo, originPoint, weakRecognizer, weakEventTracker), platformPointerArgs);
else
pointerGestureRecognizer.SendPointerPressed(view, (relativeTo) => CalculatePosition(relativeTo, originPoint, weakRecognizer, weakEventTracker), platformPointerArgs);
break;
case UIGestureRecognizerState.Changed:
if (exited)
break;

if (pointerGesture is UIHoverGestureRecognizer)
pointerGestureRecognizer.SendPointerMoved(view, (relativeTo) => CalculatePosition(relativeTo, originPoint, weakRecognizer, weakEventTracker), platformPointerArgs);
else
{
var bounds = eventTracker?.PlatformView?.Bounds;
if (bounds is not null && bounds.Value.Contains(originPoint))
pointerGestureRecognizer.SendPointerMoved(view, (relativeTo) => CalculatePosition(relativeTo, originPoint, weakRecognizer, weakEventTracker), platformPointerArgs);
else
{
pointerGestureRecognizer.SendPointerExited(view, (relativeTo) => CalculatePosition(relativeTo, originPoint, weakRecognizer, weakEventTracker), platformPointerArgs);
exited = true;
pointerGesture.State = UIGestureRecognizerState.Ended;
break;
}
}
break;
case UIGestureRecognizerState.Cancelled:
case UIGestureRecognizerState.Failed:
case UIGestureRecognizerState.Ended:
if (exited)
break;

if (pointerGesture is UIHoverGestureRecognizer)
pointerGestureRecognizer.SendPointerExited(view, (relativeTo) => CalculatePosition(relativeTo, originPoint, weakRecognizer, weakEventTracker), platformPointerArgs);
else
pointerGestureRecognizer.SendPointerReleased(view, (relativeTo) => CalculatePosition(relativeTo, originPoint, weakRecognizer, weakEventTracker), platformPointerArgs);
break;
}
}
};

var result = new List<UIGestureRecognizer?>()
{
new UIHoverGestureRecognizer((gesture) => action.Invoke(gesture)) { ShouldRecognizeSimultaneously = (g, o) => true },
new CustomPressGestureRecognizer((gesture) => action.Invoke(gesture)) { ShouldRecognizeSimultaneously = (g, o) => true }

};
return result;
}

Expand Down Expand Up @@ -593,16 +634,6 @@ void LoadRecognizers()
continue;
}

var nativeRecognizer = GetPlatformRecognizer(recognizer);

if (nativeRecognizer != null && PlatformView != null)
{
nativeRecognizer.ShouldReceiveTouch = _shouldReceiveTouch;
PlatformView.AddGestureRecognizer(nativeRecognizer);

_gestureRecognizers[recognizer] = nativeRecognizer;
}

if (OperatingSystem.IsIOSVersionAtLeast(11) && recognizer is DragGestureRecognizer)
{
dragFound = true;
Expand All @@ -627,6 +658,22 @@ void LoadRecognizers()
PlatformView.AddInteraction(interaction);
}
}

var nativeRecognizers = GetPlatformRecognizer(recognizer);

if (nativeRecognizers is null)
continue;

_gestureRecognizers[recognizer] = nativeRecognizers;
foreach (UIGestureRecognizer? nativeRecognizer in nativeRecognizers)
{
if (nativeRecognizer != null && PlatformView != null)
{
nativeRecognizer.ShouldReceiveTouch = _shouldReceiveTouch;
PlatformView.AddGestureRecognizer(nativeRecognizer);

}
}
}

if (OperatingSystem.IsIOSVersionAtLeast(11))
Expand All @@ -649,19 +696,27 @@ void LoadRecognizers()
for (int i = 0; i < toRemove.Count; i++)
{
IGestureRecognizer gestureRecognizer = toRemove[i];
var uiRecognizer = _gestureRecognizers[gestureRecognizer];
var uiRecognizers = _gestureRecognizers[gestureRecognizer];
_gestureRecognizers.Remove(gestureRecognizer);

if (PlatformView != null)
PlatformView.RemoveGestureRecognizer(uiRecognizer);

if (TryGetTapGestureRecognizer(gestureRecognizer, out TapGestureRecognizer? tapGestureRecognizer) &&
tapGestureRecognizer != null)
foreach (var uiRecognizer in uiRecognizers)
{
gestureRecognizer.PropertyChanged -= OnTapGestureRecognizerPropertyChanged;
}
if (uiRecognizer is null)
continue;

if (PlatformView != null)
{
PlatformView.RemoveGestureRecognizer(uiRecognizer);
}

uiRecognizer.Dispose();
if (TryGetTapGestureRecognizer(gestureRecognizer, out TapGestureRecognizer? tapGestureRecognizer) &&
tapGestureRecognizer != null)
{
gestureRecognizer.PropertyChanged -= OnTapGestureRecognizerPropertyChanged;
}

uiRecognizer.Dispose();
}
}

if (PlatformView != null && OperatingSystem.IsIOSVersionAtLeast(11))
Expand Down

This file was deleted.

@@ -0,0 +1,58 @@
#nullable disable
using System;
using Foundation;
using ObjCRuntime;
using UIKit;
using PreserveAttribute = Microsoft.Maui.Controls.Internals.PreserveAttribute;

namespace Microsoft.Maui.Controls.Platform.iOS;

internal class CustomPressGestureRecognizer : UIGestureRecognizer
{
NSObject _target;

public CustomPressGestureRecognizer(NSObject target, Selector action) : base(target, action)
{
_target = target;
}

public CustomPressGestureRecognizer(Action<UIGestureRecognizer> action)
: this(new Callback(action), Selector.FromHandle(Selector.GetHandle("target:"))!) { }

[Register("__UIGestureRecognizer")]
class Callback : Token
{
Action<UIGestureRecognizer> action;

internal Callback(Action<UIGestureRecognizer> action)
{
this.action = action;
}

[Export("target:")]
[Preserve(Conditional = true)]
public void Activated(UIGestureRecognizer sender)
{
if (OperatingSystem.IsIOSVersionAtLeast(13))
action(sender);
}
}

public override void TouchesBegan(NSSet touches, UIEvent evt)
{
State = UIGestureRecognizerState.Began;
base.TouchesBegan(touches, evt);
}

public override void TouchesEnded(NSSet touches, UIEvent evt)
{
State = UIGestureRecognizerState.Ended;
base.TouchesEnded(touches, evt);
}

public override void TouchesMoved(NSSet touches, UIEvent evt)
{
State = UIGestureRecognizerState.Changed;
base.TouchesMoved(touches, evt);
}
}