Browse Source

And Pinch gensture

Emmanuel Hansen 2 years ago
parent
commit
8e2f9d5346

+ 131 - 0
src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs

@@ -0,0 +1,131 @@
+using Avalonia.Input.GestureRecognizers;
+
+namespace Avalonia.Input
+{
+    public class PinchGestureRecognizer : StyledElement, IGestureRecognizer
+    {
+        private IInputElement? _target;
+        private IGestureRecognizerActionsDispatcher? _actions;
+        private float _initialDistance;
+        private IPointer? _firstContact;
+        private Point _firstPoint;
+        private IPointer? _secondContact;
+        private Point _secondPoint;
+        private Point _origin;
+
+        public void Initialize(IInputElement target, IGestureRecognizerActionsDispatcher actions)
+        {
+            _target = target;
+            _actions = actions;
+
+            _target?.AddHandler(InputElement.PointerPressedEvent, OnPointerPressed, Interactivity.RoutingStrategies.Tunnel | Interactivity.RoutingStrategies.Bubble);
+            _target?.AddHandler(InputElement.PointerReleasedEvent, OnPointerReleased, Interactivity.RoutingStrategies.Tunnel | Interactivity.RoutingStrategies.Bubble);
+        }
+
+        private void OnPointerPressed(object? sender, PointerPressedEventArgs e)
+        {
+            PointerPressed(e);
+        }
+
+        private void OnPointerReleased(object? sender, PointerReleasedEventArgs e)
+        {
+            PointerReleased(e);
+        }
+
+        public void PointerCaptureLost(IPointer pointer)
+        {
+            RemoveContact(pointer);
+        }
+
+        public void PointerMoved(PointerEventArgs e)
+        {
+            if (_target != null && _target is Visual visual)
+            {
+                if(_firstContact == e.Pointer)
+                {
+                    _firstPoint = e.GetPosition(visual);
+                }
+                else if (_secondContact == e.Pointer)
+                {
+                    _secondPoint = e.GetPosition(visual);
+                }
+                else
+                {
+                    return;
+                }
+
+                if (_firstContact != null && _secondContact != null)
+                {
+                    var distance = GetDistance(_firstPoint, _secondPoint);
+
+                    var scale = distance / _initialDistance;
+
+                    _target?.RaiseEvent(new PinchEventArgs(scale, _origin));
+                }
+            }
+        }
+
+        public void PointerPressed(PointerPressedEventArgs e)
+        {
+            if (_target != null && _target is Visual visual && (e.Pointer.Type == PointerType.Touch || e.Pointer.Type == PointerType.Pen))
+            {
+                if (_firstContact == null)
+                {
+                    _firstContact = e.Pointer;
+                    _firstPoint = e.GetPosition(visual);
+
+                    return;
+                }
+                else if (_secondContact == null && _firstContact != e.Pointer)
+                {
+                    _secondContact = e.Pointer;
+                    _secondPoint = e.GetPosition(visual);
+                }
+                else
+                {
+                    return;
+                }
+
+                if (_firstContact != null && _secondContact != null)
+                {
+                    _initialDistance = GetDistance(_firstPoint, _secondPoint);
+
+                    _origin = new Point((_firstPoint.X + _secondPoint.X) / 2.0f, (_firstPoint.Y + _secondPoint.Y) / 2.0f);
+
+                    _actions!.Capture(_firstContact, this);
+                    _actions!.Capture(_secondContact, this);
+                }
+            }
+        }
+
+        public void PointerReleased(PointerReleasedEventArgs e)
+        {
+            RemoveContact(e.Pointer);
+        }
+
+        private void RemoveContact(IPointer pointer)
+        {
+            if (_firstContact == pointer || _secondContact == pointer)
+            {
+                if (_secondContact == pointer)
+                {
+                    _secondContact = null;
+                }
+
+                if (_firstContact == pointer)
+                {
+                    _firstContact = _secondContact;
+
+                    _secondContact = null;
+                }
+                _target?.RaiseEvent(new PinchEndedEventArgs());
+            }
+        }
+
+        private float GetDistance(Point a, Point b)
+        {
+            var length = _secondPoint - _firstPoint;
+            return (float)new Vector(length.X, length.Y).Length;
+        }
+    }
+}

+ 2 - 2
src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs

@@ -68,7 +68,7 @@ namespace Avalonia.Input.GestureRecognizers
         }
 
         /// <summary>
-        /// Gets or sets a value indicating the distance to move the pointer before scrolling is started
+        /// Gets or sets a value indicating the distance the pointer moves before scrolling is started
         /// </summary>
         public int ScrollStartDistance
         {
@@ -96,7 +96,7 @@ namespace Avalonia.Input.GestureRecognizers
         }
         
         // Pixels per second speed that is considered to be the stop of inertial scroll
-        private const double InertialScrollSpeedEnd = 5;
+        private const double InertialScrollSpeedEnd = 0;
         
         public void PointerMoved(PointerEventArgs e)
         {

+ 8 - 0
src/Avalonia.Base/Input/Gestures.cs

@@ -46,6 +46,14 @@ namespace Avalonia.Input
         private static readonly WeakReference<object?> s_lastPress = new WeakReference<object?>(null);
         private static Point s_lastPressPoint;
 
+        public static readonly RoutedEvent<PinchEventArgs> PinchEvent =
+            RoutedEvent.Register<PinchEventArgs>(
+                "PinchEvent", RoutingStrategies.Bubble, typeof(Gestures));
+
+        public static readonly RoutedEvent<PinchEndedEventArgs> PinchEndedEvent =
+            RoutedEvent.Register<PinchEndedEventArgs>(
+                "PinchEndedEvent", RoutingStrategies.Bubble, typeof(Gestures));
+
         public static readonly RoutedEvent<PullGestureEventArgs> PullGestureEvent =
             RoutedEvent.Register<PullGestureEventArgs>(
                 "PullGesture", RoutingStrategies.Bubble, typeof(Gestures));

+ 24 - 0
src/Avalonia.Base/Input/PinchEventArgs.cs

@@ -0,0 +1,24 @@
+using Avalonia.Interactivity;
+
+namespace Avalonia.Input
+{
+    public class PinchEventArgs : RoutedEventArgs
+    {
+        public PinchEventArgs(double scale, Point scaleOrigin) :  base(Gestures.PinchEvent)
+        {
+            Scale = scale;
+            ScaleOrigin = scaleOrigin;
+        }
+
+        public double Scale { get; } = 1;
+
+        public Point ScaleOrigin { get; }
+    }
+
+    public class PinchEndedEventArgs : RoutedEventArgs
+    {
+        public PinchEndedEventArgs() :  base(Gestures.PinchEndedEvent)
+        {
+        }
+    }
+}