| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 | using Avalonia;using Avalonia.Input;using Avalonia.Input.GestureRecognizers;using Avalonia.Interactivity;namespace InABox.Avalonia.Components.ImageEditing;public class PanAndZoomEventArgs : RoutedEventArgs{    public PanAndZoomEventArgs(double scale, Point scaleOrigin, Vector pan) : base(PanAndZoomGestureRecognizer.PanAndZoomEvent)    {        Scale = scale;        ScaleOrigin = scaleOrigin;        Pan = pan;    }    public double Scale { get; } = 1;    public Point ScaleOrigin { get; }    public Vector Pan { get; set; }}public class PanAndZoomEndedEventArgs : RoutedEventArgs{    public PanAndZoomEndedEventArgs() : base(PanAndZoomGestureRecognizer.PanAndZoomEndedEvent)    {    }}// Adapted from the PinchGestureRecognizer built in to Avalonia, but with panning added.public class PanAndZoomGestureRecognizer : GestureRecognizer{    public static readonly RoutedEvent<PanAndZoomEventArgs> PanAndZoomEvent =        RoutedEvent.Register<PanAndZoomEventArgs>(            "PanAndZoom", RoutingStrategies.Bubble, typeof(PanAndZoomGestureRecognizer));    public static readonly RoutedEvent<PanAndZoomEndedEventArgs> PanAndZoomEndedEvent =        RoutedEvent.Register<PanAndZoomEndedEventArgs>(            "PanAndZoomEnded", RoutingStrategies.Bubble, typeof(PanAndZoomGestureRecognizer));    private float _initialDistance;    private IPointer? _firstContact;    private Point _firstPoint;    private IPointer? _secondContact;    private Point _secondPoint;    private Point _previousOrigin;    protected override void PointerCaptureLost(IPointer pointer)    {        RemoveContact(pointer);    }    protected override void PointerMoved(PointerEventArgs e)    {        if (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;                var origin = (_firstPoint + _secondPoint) / 2;                var pinchEventArgs = new PanAndZoomEventArgs(scale, origin, origin - _previousOrigin);                _previousOrigin = origin;                Target?.RaiseEvent(pinchEventArgs);                e.Handled = pinchEventArgs.Handled;                e.PreventGestureRecognition();            }        }    }    protected override void PointerPressed(PointerPressedEventArgs e)    {        if (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);                _previousOrigin = new Point((_firstPoint.X + _secondPoint.X) / 2.0f, (_firstPoint.Y + _secondPoint.Y) / 2.0f);                Capture(_firstContact);                Capture(_secondContact);                e.PreventGestureRecognition();            }        }    }    protected override void PointerReleased(PointerReleasedEventArgs e)    {        if(RemoveContact(e.Pointer))        {            e.PreventGestureRecognition();        }    }    private bool RemoveContact(IPointer pointer)    {        if (_firstContact == pointer || _secondContact == pointer)        {            if (_secondContact == pointer)            {                _secondContact = null;            }            if (_firstContact == pointer)            {                _firstContact = _secondContact;                _secondContact = null;            }            Target?.RaiseEvent(new PanAndZoomEndedEventArgs());            return true;        }        return false;    }    private static float GetDistance(Point a, Point b)    {        var length = b - a;        return (float)new Vector(length.X, length.Y).Length;    }}
 |