using System; using System.Collections.Generic; using System.Linq; using Avalonia.Input.Raw; using Avalonia.Platform; namespace Avalonia.Input { /// /// Handles raw touch events /// /// /// This class is supposed to be used on per-toplevel basis, don't use a shared one /// public class TouchDevice : IPointerDevice, IDisposable { private readonly Dictionary _pointers = new Dictionary(); private bool _disposed; private int _clickCount; private Rect _lastClickRect; private ulong _lastClickTime; RawInputModifiers GetModifiers(RawInputModifiers modifiers, bool isLeftButtonDown) { var rv = modifiers &= RawInputModifiers.KeyboardMask; if (isLeftButtonDown) rv |= RawInputModifiers.LeftMouseButton; return rv; } public void ProcessRawEvent(RawInputEventArgs ev) { if (ev.Handled || _disposed) return; var args = (RawPointerEventArgs)ev; if (!_pointers.TryGetValue(args.RawPointerId, out var pointer)) { if (args.Type == RawPointerEventType.TouchEnd) return; var hit = args.InputHitTestResult; _pointers[args.RawPointerId] = pointer = new Pointer(Pointer.GetNextFreeId(), PointerType.Touch, _pointers.Count == 0); pointer.Capture(hit); } var target = pointer.Captured ?? args.Root; var updateKind = args.Type.ToUpdateKind(); var keyModifier = args.InputModifiers.ToKeyModifiers(); if (args.Type == RawPointerEventType.TouchBegin) { if (_pointers.Count > 1) { _clickCount = 1; _lastClickTime = 0; _lastClickRect = new Rect(); } else { var settings = AvaloniaLocator.Current.GetRequiredService(); if (!_lastClickRect.Contains(args.Position) || ev.Timestamp - _lastClickTime > settings.TouchDoubleClickTime.TotalMilliseconds) { _clickCount = 0; } ++_clickCount; _lastClickTime = ev.Timestamp; _lastClickRect = new Rect(args.Position, new Size()) .Inflate(new Thickness(settings.TouchDoubleClickSize.Width / 2, settings.TouchDoubleClickSize.Height / 2)); } target.RaiseEvent(new PointerPressedEventArgs(target, pointer, args.Root, args.Position, ev.Timestamp, new PointerPointProperties(GetModifiers(args.InputModifiers, true), updateKind), keyModifier, _clickCount)); } if (args.Type == RawPointerEventType.TouchEnd) { _pointers.Remove(args.RawPointerId); using (pointer) { target.RaiseEvent(new PointerReleasedEventArgs(target, pointer, args.Root, args.Position, ev.Timestamp, new PointerPointProperties(GetModifiers(args.InputModifiers, false), updateKind), keyModifier, MouseButton.Left)); } } if (args.Type == RawPointerEventType.TouchCancel) { _pointers.Remove(args.RawPointerId); using (pointer) pointer.Capture(null); } if (args.Type == RawPointerEventType.TouchUpdate) { target.RaiseEvent(new PointerEventArgs(InputElement.PointerMovedEvent, target, pointer, args.Root, args.Position, ev.Timestamp, new PointerPointProperties(GetModifiers(args.InputModifiers, true), updateKind), keyModifier, args.IntermediatePoints)); } } public void Dispose() { if (_disposed) return; var values = _pointers.Values.ToArray(); _pointers.Clear(); _disposed = true; foreach (var p in values) p.Dispose(); } public IPointer? TryGetPointer(RawPointerEventArgs ev) { return _pointers.TryGetValue(ev.RawPointerId, out var pointer) ? pointer : null; } } }