diff --git a/src/Avalonia.Input/MouseDevice.cs b/src/Avalonia.Input/MouseDevice.cs index 34d2038d66..3749947da9 100644 --- a/src/Avalonia.Input/MouseDevice.cs +++ b/src/Avalonia.Input/MouseDevice.cs @@ -273,7 +273,7 @@ namespace Avalonia.Input } private bool MouseMove(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, PointerPointProperties properties, - KeyModifiers inputModifiers, IReadOnlyList? intermediatePoints) + KeyModifiers inputModifiers, Lazy?>? intermediatePoints) { device = device ?? throw new ArgumentNullException(nameof(device)); root = root ?? throw new ArgumentNullException(nameof(root)); diff --git a/src/Avalonia.Input/PointerEventArgs.cs b/src/Avalonia.Input/PointerEventArgs.cs index 40495a2f0a..0604d09dc4 100644 --- a/src/Avalonia.Input/PointerEventArgs.cs +++ b/src/Avalonia.Input/PointerEventArgs.cs @@ -11,7 +11,7 @@ namespace Avalonia.Input private readonly IVisual? _rootVisual; private readonly Point _rootVisualPosition; private readonly PointerPointProperties _properties; - private readonly IReadOnlyList? _previousPoints; + private Lazy?>? _previousPoints; public PointerEventArgs(RoutedEvent routedEvent, IInteractive? source, @@ -38,7 +38,7 @@ namespace Avalonia.Input ulong timestamp, PointerPointProperties properties, KeyModifiers modifiers, - IReadOnlyList? previousPoints) + Lazy?>? previousPoints) : this(routedEvent, source, pointer, rootVisual, rootVisualPosition, timestamp, properties, modifiers) { _previousPoints = previousPoints; @@ -121,13 +121,14 @@ namespace Avalonia.Input /// public IReadOnlyList GetIntermediatePoints(IVisual? relativeTo) { - if (_previousPoints == null || _previousPoints.Count == 0) + 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 points = new PointerPoint[previousPoints.Count + 1]; + for (var c = 0; c < previousPoints.Count; c++) { - var pt = _previousPoints[c]; - points[c] = new PointerPoint(Pointer, GetPosition(pt, relativeTo), _properties); + var pt = previousPoints[c]; + points[c] = new PointerPoint(Pointer, GetPosition(pt.Position, relativeTo), _properties); } points[points.Length - 1] = GetCurrentPoint(relativeTo); diff --git a/src/Avalonia.Input/Raw/RawPointerEventArgs.cs b/src/Avalonia.Input/Raw/RawPointerEventArgs.cs index 1fe19d9c55..c157fa059c 100644 --- a/src/Avalonia.Input/Raw/RawPointerEventArgs.cs +++ b/src/Avalonia.Input/Raw/RawPointerEventArgs.cs @@ -33,6 +33,8 @@ namespace Avalonia.Input.Raw /// public class RawPointerEventArgs : RawInputEventArgs { + private RawPointerPoint _point; + /// /// Initializes a new instance of the class. /// @@ -58,11 +60,50 @@ namespace Avalonia.Input.Raw Type = type; InputModifiers = inputModifiers; } + + /// + /// Initializes a new instance of the class. + /// + /// The associated device. + /// The event timestamp. + /// The root from which the event originates. + /// The type of the event. + /// The point properties and position, in client DIPs. + /// The input modifiers. + public RawPointerEventArgs( + IInputDevice device, + ulong timestamp, + IInputRoot root, + RawPointerEventType type, + RawPointerPoint point, + RawInputModifiers inputModifiers) + : base(device, timestamp, root) + { + Contract.Requires(device != null); + Contract.Requires(root != null); + + Point = point; + Type = type; + InputModifiers = inputModifiers; + } + + /// + /// Gets the pointer properties and position, in client DIPs. + /// + public RawPointerPoint Point + { + get => _point; + set => _point = value; + } /// /// Gets the mouse position, in client DIPs. /// - public Point Position { get; set; } + public Point Position + { + get => _point.Position; + set => _point.Position = value; + } /// /// Gets the type of the event. @@ -78,6 +119,19 @@ namespace Avalonia.Input.Raw /// Points that were traversed by a pointer since the previous relevant event, /// only valid for Move and TouchUpdate /// - public IReadOnlyList? IntermediatePoints { get; set; } + public Lazy?>? IntermediatePoints { get; set; } + } + + public struct RawPointerPoint + { + /// + /// Pointer position, in client DIPs. + /// + public Point Position { get; set; } + + public RawPointerPoint() + { + Position = default; + } } } diff --git a/src/Shared/RawEventGrouping.cs b/src/Shared/RawEventGrouping.cs index 25b4b41e56..084593ffc6 100644 --- a/src/Shared/RawEventGrouping.cs +++ b/src/Shared/RawEventGrouping.cs @@ -53,7 +53,7 @@ internal class RawEventGrouper : IDisposable _eventCallback?.Invoke(ev); - if (ev is RawPointerEventArgs { IntermediatePoints: PooledList list }) + if (ev is RawPointerEventArgs { IntermediatePoints.Value: PooledList list }) list.Dispose(); if (Dispatcher.UIThread.HasJobsWithPriority(DispatcherPriority.Input + 1)) @@ -110,10 +110,14 @@ internal class RawEventGrouper : IDisposable AddToQueue(args); } + private static IReadOnlyList GetPooledList() => new PooledList(); + private static readonly Func> s_getPooledListDelegate = GetPooledList; + private static void MergeEvents(RawPointerEventArgs last, RawPointerEventArgs current) { - last.IntermediatePoints ??= new PooledList(); - ((PooledList)last.IntermediatePoints).Add(last.Position); + + last.IntermediatePoints ??= new Lazy?>(s_getPooledListDelegate); + ((PooledList)last.IntermediatePoints.Value!).Add(new RawPointerPoint { Position = last.Position }); last.Position = current.Position; last.Timestamp = current.Timestamp; last.InputModifiers = current.InputModifiers;