using System; using System.Collections.Generic; using Avalonia.Input.Raw; using Avalonia.Interactivity; using Avalonia.Metadata; using Avalonia.VisualTree; namespace Avalonia.Input { public class PointerEventArgs : RoutedEventArgs { private readonly Visual? _rootVisual; private readonly Point _rootVisualPosition; private readonly PointerPointProperties _properties; private readonly Lazy?>? _previousPoints; [Unstable("This constructor might be removed in 12.0. For unit testing, consider using IHeadlessWindow mouse methods.")] public PointerEventArgs(RoutedEvent routedEvent, object? source, IPointer pointer, Visual? rootVisual, Point rootVisualPosition, ulong timestamp, PointerPointProperties properties, KeyModifiers modifiers) : base(routedEvent) { Source = source; _rootVisual = rootVisual; _rootVisualPosition = rootVisualPosition; _properties = properties; Pointer = pointer; Timestamp = timestamp; KeyModifiers = modifiers; } internal PointerEventArgs(RoutedEvent routedEvent, object? source, IPointer pointer, Visual? rootVisual, Point rootVisualPosition, ulong timestamp, PointerPointProperties properties, KeyModifiers modifiers, Lazy?>? previousPoints) #pragma warning disable CS0618 : this(routedEvent, source, pointer, rootVisual, rootVisualPosition, timestamp, properties, modifiers) #pragma warning restore CS0618 { _previousPoints = previousPoints; } /// /// Gets specific pointer generated by input device. /// public IPointer Pointer { get; } /// /// Gets the time when the input occurred. /// public ulong Timestamp { get; } internal bool IsGestureRecognitionSkipped { get; private set; } /// /// Gets a value that indicates which key modifiers were active at the time that the pointer event was initiated. /// public KeyModifiers KeyModifiers { get; } private Point GetPosition(Point pt, Visual? relativeTo) { if (_rootVisual == null) return default; if (relativeTo == null) return pt; // If the visual the user passed in, is not connected to the same visual root // (i.e. they called it for a control inside a popup. if (!ReferenceEquals(_rootVisual, relativeTo.VisualRoot) && relativeTo.VisualRoot is { }) { // Convert to absolute screen coordinates. var screenPt = _rootVisual.PointToScreen(pt); // Convert to client co-ordinates of the visual inside the other visual root. return relativeTo.PointToClient(screenPt); } return pt * _rootVisual.TransformToVisual(relativeTo) ?? default; } /// /// Gets the pointer position relative to a control. /// /// The visual whose coordinate system to use. Pass null for toplevel coordinate system /// The pointer position in the control's coordinates. public Point GetPosition(Visual? relativeTo) => GetPosition(_rootVisualPosition, relativeTo); /// /// Returns the PointerPoint associated with the current event /// /// The visual whose coordinate system to use. Pass null for toplevel coordinate system /// public PointerPoint GetCurrentPoint(Visual? relativeTo) => new PointerPoint(Pointer, GetPosition(relativeTo), _properties); /// /// Returns the PointerPoint associated with the current event /// /// The visual which coordinate system to use. Pass null for toplevel coordinate system /// public IReadOnlyList GetIntermediatePoints(Visual? relativeTo) { var previousPoints = _previousPoints?.Value; if (previousPoints == null || previousPoints.Count == 0) return new[] { GetCurrentPoint(relativeTo) }; var points = new PointerPoint[previousPoints.Count + 1]; for (var c = 0; c < previousPoints.Count; c++) { var pt = previousPoints[c]; var pointProperties = new PointerPointProperties(_properties, pt); points[c] = new PointerPoint(Pointer, GetPosition(pt.Position, relativeTo), pointProperties); } points[points.Length - 1] = GetCurrentPoint(relativeTo); return points; } /// /// Prevents this event from being handled by other gesture recognizers in the route /// public void PreventGestureRecognition() { IsGestureRecognitionSkipped = true; } /// /// Returns the current pointer point properties /// protected PointerPointProperties Properties => _properties; } public enum MouseButton { None, Left, Right, Middle, XButton1, XButton2 } public class PointerPressedEventArgs : PointerEventArgs { [Unstable("This constructor might be removed in 12.0. For unit testing, consider using IHeadlessWindow mouse methods.")] public PointerPressedEventArgs( object source, IPointer pointer, Visual rootVisual, Point rootVisualPosition, ulong timestamp, PointerPointProperties properties, KeyModifiers modifiers, int clickCount = 1) : base(InputElement.PointerPressedEvent, source, pointer, rootVisual, rootVisualPosition, timestamp, properties, modifiers) { ClickCount = clickCount; } public int ClickCount { get; } } public class PointerReleasedEventArgs : PointerEventArgs { [Unstable("This constructor might be removed in 12.0. For unit testing, consider using IHeadlessWindow mouse methods.")] public PointerReleasedEventArgs( object source, IPointer pointer, Visual rootVisual, Point rootVisualPosition, ulong timestamp, PointerPointProperties properties, KeyModifiers modifiers, MouseButton initialPressMouseButton) : base(InputElement.PointerReleasedEvent, source, pointer, rootVisual, rootVisualPosition, timestamp, properties, modifiers) { InitialPressMouseButton = initialPressMouseButton; } /// /// Gets the mouse button that triggered the corresponding PointerPressed event /// public MouseButton InitialPressMouseButton { get; } } public class PointerCaptureLostEventArgs : RoutedEventArgs { public IPointer Pointer { get; } [Unstable("This constructor might be removed in 12.0. If you need to remove capture, use stable methods on the IPointer instance.,")] public PointerCaptureLostEventArgs(object source, IPointer pointer) : base(InputElement.PointerCaptureLostEvent) { Pointer = pointer; Source = source; } } }