From f715f3e7b7f2acb142010fdb09b0c78aecb0be21 Mon Sep 17 00:00:00 2001 From: libin Date: Wed, 25 Sep 2024 16:37:36 +0800 Subject: [PATCH] implement basic mouse support for macOS --- .../AppHostBuilderExtensions.cs | 2 +- .../Effects/PlatformTouchEffect.cs | 129 ++++++++++++++++++ Source/OxyPlot.Maui.Skia/PlotController.cs | 7 +- 3 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 Source/OxyPlot.Maui.Skia/Platforms/MacCatalyst/Effects/PlatformTouchEffect.cs diff --git a/Source/OxyPlot.Maui.Skia/AppHostBuilderExtensions.cs b/Source/OxyPlot.Maui.Skia/AppHostBuilderExtensions.cs index aa827ff..c75b15e 100644 --- a/Source/OxyPlot.Maui.Skia/AppHostBuilderExtensions.cs +++ b/Source/OxyPlot.Maui.Skia/AppHostBuilderExtensions.cs @@ -14,7 +14,7 @@ public static MauiAppBuilder UseOxyPlotSkia(this MauiAppBuilder builder) #elif WINDOWS effects.Add(); #elif MACCATALYST - // not implemented + effects.Add(); #elif __IOS__ effects.Add(); #endif diff --git a/Source/OxyPlot.Maui.Skia/Platforms/MacCatalyst/Effects/PlatformTouchEffect.cs b/Source/OxyPlot.Maui.Skia/Platforms/MacCatalyst/Effects/PlatformTouchEffect.cs new file mode 100644 index 0000000..255cad3 --- /dev/null +++ b/Source/OxyPlot.Maui.Skia/Platforms/MacCatalyst/Effects/PlatformTouchEffect.cs @@ -0,0 +1,129 @@ +using CoreGraphics; +using Foundation; +using Microsoft.Maui.Controls.Platform; +using OxyPlot.Maui.Skia.Effects; +using UIKit; + +namespace OxyPlot.Maui.Skia.macOS.Effects; + +public class PlatformTouchEffect : PlatformEffect +{ + private UIView _view; + private TouchRecognizer _touchRecognizer; + private MyTouchEffect _touchEffect; + protected override void OnAttached() + { + _view = Control ?? Container; + _touchEffect = Element.Effects.OfType().FirstOrDefault(); + + if (_touchEffect == null || _view == null) return; + + _touchRecognizer = new TouchRecognizer(Element, _touchEffect); + _view.AddGestureRecognizer(_touchRecognizer); + _view.AddGestureRecognizer(GetMouseWheelRecognizer(_view)); + } + + protected override void OnDetached() + { + if (_touchRecognizer != null) + { + _touchRecognizer.Detach(); + _view.RemoveGestureRecognizer(_touchRecognizer); + } + } + + // https://github.com/dotnet/maui/issues/16130 + private UIPanGestureRecognizer GetMouseWheelRecognizer(UIView v) + { + return new UIPanGestureRecognizer((e) => + { + if (e.State == UIGestureRecognizerState.Ended) + return; + + var isZoom = e.NumberOfTouches == 0; + if (!isZoom) return; + + var l = e.LocationInView(v); + var t = e.TranslationInView(v); + var deltaX = t.X / 2; + var deltaY = t.Y / 2; + var delta = deltaY != 0 ? deltaY : deltaX; + + var tolerance = 5; + if (Math.Abs(delta) < tolerance) return; + + var pointerX = l.X - t.X; + var pointerY = l.Y - t.Y; + var locations = new[] { new Point(pointerX, pointerY) }; + + var eventArgs = new TouchActionEventArgs(0, TouchActionType.MouseWheel, locations, false) + { + MouseWheelDelta = (int)delta + }; + + _touchEffect.OnTouchAction(Element, eventArgs); + }) + { + AllowedScrollTypesMask = UIScrollTypeMask.Discrete | UIScrollTypeMask.Continuous, + MinimumNumberOfTouches = 0, + ShouldRecognizeSimultaneously = (_, _) => true + }; + } +} + +internal class TouchRecognizer : UIGestureRecognizer +{ + private readonly Microsoft.Maui.Controls.Element _element; + private readonly MyTouchEffect _touchEffect; + private uint _activeTouchesCount = 0; + + public TouchRecognizer(Microsoft.Maui.Controls.Element element, MyTouchEffect touchEffect) + { + this._element = element; + this._touchEffect = touchEffect; + ShouldRecognizeSimultaneously = (_, _) => true; + } + + public void Detach() + { + ShouldRecognizeSimultaneously = null; + } + + public override void TouchesBegan(NSSet touches, UIEvent evt) + { + base.TouchesBegan(touches, evt); + _activeTouchesCount += touches.Count.ToUInt32(); + FireEvent(touches, TouchActionType.Pressed, true); + } + + public override void TouchesMoved(NSSet touches, UIEvent evt) + { + base.TouchesMoved(touches, evt); + + if (_activeTouchesCount == touches.Count.ToUInt32()) + { + FireEvent(touches, TouchActionType.Moved, true); + } + } + + public override void TouchesEnded(NSSet touches, UIEvent evt) + { + base.TouchesEnded(touches, evt); + _activeTouchesCount -= touches.Count.ToUInt32(); + FireEvent(touches, TouchActionType.Released, false); + } + + private void FireEvent(NSSet touches, TouchActionType actionType, bool isInContact) + { + UITouch[] uiTouches = touches.Cast().ToArray(); + long id = ((IntPtr)uiTouches.First().Handle).ToInt64(); + Point[] points = new Point[uiTouches.Length]; + + for (int i = 0; i < uiTouches.Length; i++) + { + CGPoint cgPoint = uiTouches[i].LocationInView(View); + points[i] = new(cgPoint.X, cgPoint.Y); + } + _touchEffect.OnTouchAction(_element, new(id, actionType, points, isInContact)); + } +} \ No newline at end of file diff --git a/Source/OxyPlot.Maui.Skia/PlotController.cs b/Source/OxyPlot.Maui.Skia/PlotController.cs index fbb96ef..e3ac151 100644 --- a/Source/OxyPlot.Maui.Skia/PlotController.cs +++ b/Source/OxyPlot.Maui.Skia/PlotController.cs @@ -17,8 +17,11 @@ public PlotController() this.BindTouchDown(cmd); #if WINDOWS - this.BindMouseWheel(OxyPlot.PlotCommands.ZoomWheel); - this.BindMouseWheel(OxyModifierKeys.Control, OxyPlot.PlotCommands.ZoomWheelFine); + this.BindMouseWheel(OxyPlot.PlotCommands.ZoomWheel); + this.BindMouseWheel(OxyModifierKeys.Control, OxyPlot.PlotCommands.ZoomWheelFine); +#endif +#if MACCATALYST + this.BindMouseWheel(OxyPlot.PlotCommands.ZoomWheel); #endif } } \ No newline at end of file