Gestures.cs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. using System;
  2. using Avalonia.Interactivity;
  3. using Avalonia.Platform;
  4. using Avalonia.VisualTree;
  5. namespace Avalonia.Input
  6. {
  7. public static class Gestures
  8. {
  9. private static bool s_isDoubleTapped = false;
  10. public static readonly RoutedEvent<TappedEventArgs> TappedEvent = RoutedEvent.Register<TappedEventArgs>(
  11. "Tapped",
  12. RoutingStrategies.Bubble,
  13. typeof(Gestures));
  14. public static readonly RoutedEvent<TappedEventArgs> DoubleTappedEvent = RoutedEvent.Register<TappedEventArgs>(
  15. "DoubleTapped",
  16. RoutingStrategies.Bubble,
  17. typeof(Gestures));
  18. public static readonly RoutedEvent<TappedEventArgs> RightTappedEvent = RoutedEvent.Register<TappedEventArgs>(
  19. "RightTapped",
  20. RoutingStrategies.Bubble,
  21. typeof(Gestures));
  22. public static readonly RoutedEvent<ScrollGestureEventArgs> ScrollGestureEvent =
  23. RoutedEvent.Register<ScrollGestureEventArgs>(
  24. "ScrollGesture", RoutingStrategies.Bubble, typeof(Gestures));
  25. public static readonly RoutedEvent<ScrollGestureEndedEventArgs> ScrollGestureEndedEvent =
  26. RoutedEvent.Register<ScrollGestureEndedEventArgs>(
  27. "ScrollGestureEnded", RoutingStrategies.Bubble, typeof(Gestures));
  28. public static readonly RoutedEvent<PointerDeltaEventArgs> PointerTouchPadGestureMagnifyEvent =
  29. RoutedEvent.Register<PointerDeltaEventArgs>(
  30. "PointerMagnifyGesture", RoutingStrategies.Bubble, typeof(Gestures));
  31. public static readonly RoutedEvent<PointerDeltaEventArgs> PointerTouchPadGestureRotateEvent =
  32. RoutedEvent.Register<PointerDeltaEventArgs>(
  33. "PointerRotateGesture", RoutingStrategies.Bubble, typeof(Gestures));
  34. public static readonly RoutedEvent<PointerDeltaEventArgs> PointerTouchPadGestureSwipeEvent =
  35. RoutedEvent.Register<PointerDeltaEventArgs>(
  36. "PointerSwipeGesture", RoutingStrategies.Bubble, typeof(Gestures));
  37. private static readonly WeakReference<object?> s_lastPress = new WeakReference<object?>(null);
  38. private static Point s_lastPressPoint;
  39. public static readonly RoutedEvent<PullGestureEventArgs> PullGestureEvent =
  40. RoutedEvent.Register<PullGestureEventArgs>(
  41. "PullGesture", RoutingStrategies.Bubble, typeof(Gestures));
  42. public static readonly RoutedEvent<PullGestureEndedEventArgs> PullGestureEndedEvent =
  43. RoutedEvent.Register<PullGestureEndedEventArgs>(
  44. "PullGestureEnded", RoutingStrategies.Bubble, typeof(Gestures));
  45. static Gestures()
  46. {
  47. InputElement.PointerPressedEvent.RouteFinished.Subscribe(PointerPressed);
  48. InputElement.PointerReleasedEvent.RouteFinished.Subscribe(PointerReleased);
  49. }
  50. public static void AddTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
  51. {
  52. element.AddHandler(TappedEvent, handler);
  53. }
  54. public static void AddDoubleTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
  55. {
  56. element.AddHandler(DoubleTappedEvent, handler);
  57. }
  58. public static void AddRightTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
  59. {
  60. element.AddHandler(RightTappedEvent, handler);
  61. }
  62. public static void RemoveTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
  63. {
  64. element.RemoveHandler(TappedEvent, handler);
  65. }
  66. public static void RemoveDoubleTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
  67. {
  68. element.RemoveHandler(DoubleTappedEvent, handler);
  69. }
  70. public static void RemoveRightTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
  71. {
  72. element.RemoveHandler(RightTappedEvent, handler);
  73. }
  74. private static void PointerPressed(RoutedEventArgs ev)
  75. {
  76. if (ev.Source is null)
  77. {
  78. return;
  79. }
  80. if (ev.Route == RoutingStrategies.Bubble)
  81. {
  82. var e = (PointerPressedEventArgs)ev;
  83. var visual = (Visual)ev.Source;
  84. if (e.ClickCount % 2 == 1)
  85. {
  86. s_isDoubleTapped = false;
  87. s_lastPress.SetTarget(ev.Source);
  88. s_lastPressPoint = e.GetPosition((Visual)ev.Source);
  89. }
  90. else if (e.ClickCount % 2 == 0 && e.GetCurrentPoint(visual).Properties.IsLeftButtonPressed)
  91. {
  92. if (s_lastPress.TryGetTarget(out var target) &&
  93. target == e.Source &&
  94. e.Source is Interactive i)
  95. {
  96. s_isDoubleTapped = true;
  97. i.RaiseEvent(new TappedEventArgs(DoubleTappedEvent, e));
  98. }
  99. }
  100. }
  101. }
  102. private static void PointerReleased(RoutedEventArgs ev)
  103. {
  104. if (ev.Route == RoutingStrategies.Bubble)
  105. {
  106. var e = (PointerReleasedEventArgs)ev;
  107. if (s_lastPress.TryGetTarget(out var target) &&
  108. target == e.Source &&
  109. e.InitialPressMouseButton is MouseButton.Left or MouseButton.Right &&
  110. e.Source is Interactive i)
  111. {
  112. var point = e.GetCurrentPoint((Visual)target);
  113. var settings = AvaloniaLocator.Current.GetService<IPlatformSettings>();
  114. var tapSize = settings?.GetTapSize(point.Pointer.Type) ?? new Size(4, 4);
  115. var tapRect = new Rect(s_lastPressPoint, new Size())
  116. .Inflate(new Thickness(tapSize.Width, tapSize.Height));
  117. if (tapRect.ContainsExclusive(point.Position))
  118. {
  119. if (e.InitialPressMouseButton == MouseButton.Right)
  120. {
  121. i.RaiseEvent(new TappedEventArgs(RightTappedEvent, e));
  122. }
  123. //s_isDoubleTapped needed here to prevent invoking Tapped event when DoubleTapped is called.
  124. //This behaviour matches UWP behaviour.
  125. else if (s_isDoubleTapped == false)
  126. {
  127. i.RaiseEvent(new TappedEventArgs(TappedEvent, e));
  128. }
  129. }
  130. }
  131. }
  132. }
  133. }
  134. }