diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 0b88908252..40180274e1 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -1627,6 +1627,19 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent return; } } + else if (type == Magnify) + { + delta.X = delta.Y = [event magnification]; + } + else if (type == Rotate) + { + delta.X = delta.Y = [event rotation]; + } + else if (type == Swipe) + { + delta.X = [event deltaX]; + delta.Y = [event deltaY]; + } auto timestamp = [event timestamp] * 1000; auto modifiers = [self getModifiers:[event modifierFlags]]; @@ -1753,6 +1766,24 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent [super scrollWheel:event]; } +- (void)magnifyWithEvent:(NSEvent *)event +{ + [self mouseEvent:event withType:Magnify]; + [super magnifyWithEvent:event]; +} + +- (void)rotateWithEvent:(NSEvent *)event +{ + [self mouseEvent:event withType:Rotate]; + [super rotateWithEvent:event]; +} + +- (void)swipeWithEvent:(NSEvent *)event +{ + [self mouseEvent:event withType:Swipe]; + [super swipeWithEvent:event]; +} + - (void)mouseEntered:(NSEvent *)event { _isMouseOver = true; diff --git a/src/Avalonia.Input/Gestures.cs b/src/Avalonia.Input/Gestures.cs index a5ee558f47..86e6e96a71 100644 --- a/src/Avalonia.Input/Gestures.cs +++ b/src/Avalonia.Input/Gestures.cs @@ -29,6 +29,18 @@ namespace Avalonia.Input public static readonly RoutedEvent ScrollGestureEndedEvent = RoutedEvent.Register( "ScrollGestureEnded", RoutingStrategies.Bubble, typeof(Gestures)); + + public static readonly RoutedEvent PointerTouchPadGestureMagnifyEvent = + RoutedEvent.Register( + "PointerMagnifyGesture", RoutingStrategies.Bubble, typeof(Gestures)); + + public static readonly RoutedEvent PointerTouchPadGestureRotateEvent = + RoutedEvent.Register( + "PointerRotateGesture", RoutingStrategies.Bubble, typeof(Gestures)); + + public static readonly RoutedEvent PointerTouchPadGestureSwipeEvent = + RoutedEvent.Register( + "PointerSwipeGesture", RoutingStrategies.Bubble, typeof(Gestures)); #pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. private static readonly WeakReference s_lastPress = new WeakReference(null); diff --git a/src/Avalonia.Input/InputElement.cs b/src/Avalonia.Input/InputElement.cs index def0a26b27..5aa1f1ff44 100644 --- a/src/Avalonia.Input/InputElement.cs +++ b/src/Avalonia.Input/InputElement.cs @@ -196,7 +196,7 @@ namespace Avalonia.Input /// Defines the event. /// public static readonly RoutedEvent DoubleTappedEvent = Gestures.DoubleTappedEvent; - + private bool _isEffectivelyEnabled = true; private bool _isFocused; private bool _isKeyboardFocusWithin; @@ -349,14 +349,14 @@ namespace Avalonia.Input } /// - /// Occurs when the mouse wheen is scrolled over the control. + /// Occurs when the mouse is scrolled over the control. /// public event EventHandler PointerWheelChanged { add { AddHandler(PointerWheelChangedEvent, value); } remove { RemoveHandler(PointerWheelChangedEvent, value); } } - + /// /// Occurs when a tap gesture occurs on the control. /// diff --git a/src/Avalonia.Input/MouseDevice.cs b/src/Avalonia.Input/MouseDevice.cs index a2c43013fd..087a806f77 100644 --- a/src/Avalonia.Input/MouseDevice.cs +++ b/src/Avalonia.Input/MouseDevice.cs @@ -180,6 +180,15 @@ namespace Avalonia.Input case RawPointerEventType.Wheel: e.Handled = MouseWheel(mouse, e.Timestamp, e.Root, e.Position, props, ((RawMouseWheelEventArgs)e).Delta, keyModifiers); break; + case RawPointerEventType.Magnify: + e.Handled = GestureMagnify(mouse, e.Timestamp, e.Root, e.Position, props, ((RawPointerGestureEventArgs)e).Delta, keyModifiers); + break; + case RawPointerEventType.Rotate: + e.Handled = GestureRotate(mouse, e.Timestamp, e.Root, e.Position, props, ((RawPointerGestureEventArgs)e).Delta, keyModifiers); + break; + case RawPointerEventType.Swipe: + e.Handled = GestureSwipe(mouse, e.Timestamp, e.Root, e.Position, props, ((RawPointerGestureEventArgs)e).Delta, keyModifiers); + break; } } @@ -334,6 +343,69 @@ namespace Avalonia.Input return false; } + + private bool GestureMagnify(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, + PointerPointProperties props, Vector delta, KeyModifiers inputModifiers) + { + device = device ?? throw new ArgumentNullException(nameof(device)); + root = root ?? throw new ArgumentNullException(nameof(root)); + + var hit = HitTest(root, p); + + if (hit != null) + { + var source = GetSource(hit); + var e = new PointerDeltaEventArgs(Gestures.PointerTouchPadGestureMagnifyEvent, source, + _pointer, root, p, timestamp, props, inputModifiers, delta); + + source?.RaiseEvent(e); + return e.Handled; + } + + return false; + } + + private bool GestureRotate(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, + PointerPointProperties props, Vector delta, KeyModifiers inputModifiers) + { + device = device ?? throw new ArgumentNullException(nameof(device)); + root = root ?? throw new ArgumentNullException(nameof(root)); + + var hit = HitTest(root, p); + + if (hit != null) + { + var source = GetSource(hit); + var e = new PointerDeltaEventArgs(Gestures.PointerTouchPadGestureRotateEvent, source, + _pointer, root, p, timestamp, props, inputModifiers, delta); + + source?.RaiseEvent(e); + return e.Handled; + } + + return false; + } + + private bool GestureSwipe(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, + PointerPointProperties props, Vector delta, KeyModifiers inputModifiers) + { + device = device ?? throw new ArgumentNullException(nameof(device)); + root = root ?? throw new ArgumentNullException(nameof(root)); + + var hit = HitTest(root, p); + + if (hit != null) + { + var source = GetSource(hit); + var e = new PointerDeltaEventArgs(Gestures.PointerTouchPadGestureSwipeEvent, source, + _pointer, root, p, timestamp, props, inputModifiers, delta); + + source?.RaiseEvent(e); + return e.Handled; + } + + return false; + } private IInteractive? GetSource(IVisual? hit) { diff --git a/src/Avalonia.Input/PointerDeltaEventArgs.cs b/src/Avalonia.Input/PointerDeltaEventArgs.cs new file mode 100644 index 0000000000..b3085a038d --- /dev/null +++ b/src/Avalonia.Input/PointerDeltaEventArgs.cs @@ -0,0 +1,19 @@ +using Avalonia.Interactivity; +using Avalonia.VisualTree; + +namespace Avalonia.Input +{ + public class PointerDeltaEventArgs : PointerEventArgs + { + public Vector Delta { get; set; } + + public PointerDeltaEventArgs(RoutedEvent routedEvent, IInteractive? source, + IPointer pointer, IVisual rootVisual, Point rootVisualPosition, ulong timestamp, + PointerPointProperties properties, KeyModifiers modifiers, Vector delta) + : base(routedEvent, source, pointer, rootVisual, rootVisualPosition, + timestamp, properties, modifiers) + { + Delta = delta; + } + } +} diff --git a/src/Avalonia.Input/Raw/RawPointerEventArgs.cs b/src/Avalonia.Input/Raw/RawPointerEventArgs.cs index 62a1dd5d84..d6406121c7 100644 --- a/src/Avalonia.Input/Raw/RawPointerEventArgs.cs +++ b/src/Avalonia.Input/Raw/RawPointerEventArgs.cs @@ -21,7 +21,10 @@ namespace Avalonia.Input.Raw TouchBegin, TouchUpdate, TouchEnd, - TouchCancel + TouchCancel, + Magnify, + Rotate, + Swipe } /// diff --git a/src/Avalonia.Input/Raw/RawPointerGestureEventArgs.cs b/src/Avalonia.Input/Raw/RawPointerGestureEventArgs.cs new file mode 100644 index 0000000000..5a6dbda9a7 --- /dev/null +++ b/src/Avalonia.Input/Raw/RawPointerGestureEventArgs.cs @@ -0,0 +1,19 @@ +namespace Avalonia.Input.Raw +{ + public class RawPointerGestureEventArgs : RawPointerEventArgs + { + public RawPointerGestureEventArgs( + IInputDevice device, + ulong timestamp, + IInputRoot root, + RawPointerEventType gestureType, + Point position, + Vector delta, RawInputModifiers inputModifiers) + : base(device, timestamp, root, gestureType, position, inputModifiers) + { + Delta = delta; + } + + public Vector Delta { get; private set; } + } +} diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index 87b7a7608e..1917b1575d 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -306,11 +306,28 @@ namespace Avalonia.Native switch (type) { case AvnRawMouseEventType.Wheel: - Input?.Invoke(new RawMouseWheelEventArgs(_mouse, timeStamp, _inputRoot, point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (RawInputModifiers)modifiers)); + Input?.Invoke(new RawMouseWheelEventArgs(_mouse, timeStamp, _inputRoot, + point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (RawInputModifiers)modifiers)); + break; + + case AvnRawMouseEventType.Magnify: + Input?.Invoke(new RawPointerGestureEventArgs(_mouse, timeStamp, _inputRoot, RawPointerEventType.Magnify, + point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (RawInputModifiers)modifiers)); + break; + + case AvnRawMouseEventType.Rotate: + Input?.Invoke(new RawPointerGestureEventArgs(_mouse, timeStamp, _inputRoot, RawPointerEventType.Rotate, + point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (RawInputModifiers)modifiers)); + break; + + case AvnRawMouseEventType.Swipe: + Input?.Invoke(new RawPointerGestureEventArgs(_mouse, timeStamp, _inputRoot, RawPointerEventType.Swipe, + point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (RawInputModifiers)modifiers)); break; default: - var e = new RawPointerEventArgs(_mouse, timeStamp, _inputRoot, (RawPointerEventType)type, point.ToAvaloniaPoint(), (RawInputModifiers)modifiers); + var e = new RawPointerEventArgs(_mouse, timeStamp, _inputRoot, (RawPointerEventType)type, + point.ToAvaloniaPoint(), (RawInputModifiers)modifiers); if(!ChromeHitTest(e)) { diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl index 112b4f636c..f2b9d4997e 100644 --- a/src/Avalonia.Native/avn.idl +++ b/src/Avalonia.Native/avn.idl @@ -295,7 +295,10 @@ enum AvnRawMouseEventType TouchBegin, TouchUpdate, TouchEnd, - TouchCancel + TouchCancel, + Magnify, + Rotate, + Swipe, } enum AvnRawKeyEventType