Browse Source

rework gesture routing #11522

pull/11518/head
Emmanuel Hansen 3 years ago
parent
commit
8c0dfbaf08
  1. 50
      src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs
  2. 1
      src/Avalonia.Base/Input/GestureRecognizers/IGestureRecognizer.cs
  3. 4
      src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs
  4. 6
      src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs
  5. 6
      src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs
  6. 46
      src/Avalonia.Base/Input/InputElement.cs
  7. 36
      src/Avalonia.Base/Input/MouseDevice.cs
  8. 35
      src/Avalonia.Base/Input/PenDevice.cs
  9. 5
      src/Avalonia.Base/Input/Pointer.cs
  10. 10
      src/Avalonia.Base/Input/PointerEventArgs.cs
  11. 35
      src/Avalonia.Base/Input/TouchDevice.cs
  12. 2
      src/Avalonia.Controls/Primitives/Thumb.cs

50
src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs

@ -10,8 +10,6 @@ namespace Avalonia.Input.GestureRecognizers
{
private readonly IInputElement _inputElement;
private List<IGestureRecognizer>? _recognizers;
private Dictionary<IPointer, IGestureRecognizer>? _pointerGrabs;
public GestureRecognizerCollection(IInputElement inputElement)
{
@ -24,7 +22,6 @@ namespace Avalonia.Input.GestureRecognizers
{
// We initialize the collection when the first recognizer is added
_recognizers = new List<IGestureRecognizer>();
_pointerGrabs = new Dictionary<IPointer, IGestureRecognizer>();
}
_recognizers.Add(recognizer);
@ -57,8 +54,6 @@ namespace Avalonia.Input.GestureRecognizers
return false;
foreach (var r in _recognizers)
{
if (e.Handled)
break;
r.PointerPressed(e);
}
@ -69,17 +64,15 @@ namespace Avalonia.Input.GestureRecognizers
{
if (_recognizers == null)
return false;
if (_pointerGrabs!.TryGetValue(e.Pointer, out var capture))
var pointer = e.Pointer as Pointer;
foreach (var r in _recognizers)
{
capture.PointerReleased(e);
if (pointer?.CapturedGestureRecognizer != null)
break;
r.PointerReleased(e);
}
else
foreach (var r in _recognizers)
{
if (e.Handled)
break;
r.PointerReleased(e);
}
return e.Handled;
}
@ -87,41 +80,24 @@ namespace Avalonia.Input.GestureRecognizers
{
if (_recognizers == null)
return false;
if (_pointerGrabs!.TryGetValue(e.Pointer, out var capture))
{
capture.PointerMoved(e);
}
else
foreach (var r in _recognizers)
{
if (e.Handled)
break;
r.PointerMoved(e);
}
return e.Handled;
}
var pointer = e.Pointer as Pointer;
internal void HandlePointerCaptureLost(PointerCaptureLostEventArgs e)
{
if (_recognizers == null)
return;
_pointerGrabs!.Remove(e.Pointer);
foreach (var r in _recognizers)
{
r.PointerCaptureLost(e.Pointer);
if (pointer?.CapturedGestureRecognizer != null)
break;
r.PointerMoved(e);
}
return e.Handled;
}
void IGestureRecognizerActionsDispatcher.Capture(IPointer pointer, IGestureRecognizer recognizer)
{
var p = pointer as Pointer;
if (p != null && p.CapturedGestureRecognizer != null && recognizer != p.CapturedGestureRecognizer)
return;
pointer.Capture(_inputElement);
p?.CaptureGestureRecognizer(recognizer);
_pointerGrabs![pointer] = recognizer;
foreach (var r in _recognizers!)
{
if (r != recognizer)

1
src/Avalonia.Base/Input/GestureRecognizers/IGestureRecognizer.cs

@ -2,6 +2,7 @@ namespace Avalonia.Input.GestureRecognizers
{
public interface IGestureRecognizer
{
IInputElement? Target { get; }
void Initialize(IInputElement target, IGestureRecognizerActionsDispatcher actions);
void PointerPressed(PointerPressedEventArgs e);
void PointerReleased(PointerReleasedEventArgs e);

4
src/Avalonia.Base/Input/GestureRecognizers/PinchGestureRecognizer.cs

@ -2,7 +2,7 @@
namespace Avalonia.Input
{
public class PinchGestureRecognizer : StyledElement, IGestureRecognizer
public class PinchGestureRecognizer : AvaloniaObject, IGestureRecognizer
{
private IInputElement? _target;
private IGestureRecognizerActionsDispatcher? _actions;
@ -13,6 +13,8 @@ namespace Avalonia.Input
private Point _secondPoint;
private Point _origin;
public IInputElement? Target => _target;
public void Initialize(IInputElement target, IGestureRecognizerActionsDispatcher actions)
{
_target = target;

6
src/Avalonia.Base/Input/GestureRecognizers/PullGestureRecognizer.cs

@ -1,10 +1,9 @@
using System;
using System.Diagnostics;
using Avalonia.Input.GestureRecognizers;
namespace Avalonia.Input
{
public class PullGestureRecognizer : StyledElement, IGestureRecognizer
public class PullGestureRecognizer : AvaloniaObject, IGestureRecognizer
{
internal static int MinPullDetectionSize = 50;
@ -27,6 +26,8 @@ namespace Avalonia.Input
set => SetValue(PullDirectionProperty, value);
}
public IInputElement? Target => _target;
public PullGestureRecognizer(PullDirection pullDirection)
{
PullDirection = pullDirection;
@ -54,6 +55,7 @@ namespace Avalonia.Input
{
var currentPosition = e.GetPosition(visual);
_actions!.Capture(e.Pointer, this);
e.PreventGestureRecognition();
Vector delta = default;
switch (PullDirection)

6
src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs

@ -91,7 +91,9 @@ namespace Avalonia.Input.GestureRecognizers
{
get => _scrollStartDistance;
set => SetAndRaise(ScrollStartDistanceProperty, ref _scrollStartDistance, value);
}
}
public IInputElement? Target => _target;
public void Initialize(IInputElement target, IGestureRecognizerActionsDispatcher actions)
{
@ -132,6 +134,8 @@ namespace Avalonia.Input.GestureRecognizers
_trackedRootPoint.Y - (_trackedRootPoint.Y >= rootPoint.Y ? ScrollStartDistance : -ScrollStartDistance));
_actions!.Capture(e.Pointer, this);
e.PreventGestureRecognition();
}
}

46
src/Avalonia.Base/Input/InputElement.cs

@ -225,6 +225,11 @@ namespace Avalonia.Input
PointerReleasedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerReleased(e));
PointerCaptureLostEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerCaptureLost(e));
PointerWheelChangedEvent.AddClassHandler<InputElement>((x, e) => x.OnPointerWheelChanged(e));
// Gesture only handlers
PointerMovedEvent.AddClassHandler<InputElement>((x, e) => x.OnGesturePointerMoved(e), handledEventsToo: true);
PointerPressedEvent.AddClassHandler<InputElement>((x, e) => x.OnGesturePointerPressed(e), handledEventsToo: true);
PointerReleasedEvent.AddClassHandler<InputElement>((x, e) => x.OnGesturePointerReleased(e), handledEventsToo: true);
}
public InputElement()
@ -583,10 +588,6 @@ namespace Avalonia.Input
/// <param name="e">The event args.</param>
protected virtual void OnPointerMoved(PointerEventArgs e)
{
if (_gestureRecognizers?.HandlePointerMoved(e) == true)
{
e.Handled = true;
}
}
/// <summary>
@ -595,10 +596,6 @@ namespace Avalonia.Input
/// <param name="e">The event args.</param>
protected virtual void OnPointerPressed(PointerPressedEventArgs e)
{
if (_gestureRecognizers?.HandlePointerPressed(e) == true)
{
e.Handled = true;
}
}
/// <summary>
@ -607,10 +604,33 @@ namespace Avalonia.Input
/// <param name="e">The event args.</param>
protected virtual void OnPointerReleased(PointerReleasedEventArgs e)
{
if (_gestureRecognizers?.HandlePointerReleased(e) == true)
{
e.Handled = true;
}
}
private void OnGesturePointerReleased(PointerReleasedEventArgs e)
{
if (!e.IsGestureRecognitionSkipped)
if (_gestureRecognizers?.HandlePointerReleased(e) == true)
{
e.Handled = true;
}
}
private void OnGesturePointerPressed(PointerPressedEventArgs e)
{
if (!e.IsGestureRecognitionSkipped)
if (_gestureRecognizers?.HandlePointerPressed(e) == true)
{
e.Handled = true;
}
}
private void OnGesturePointerMoved(PointerEventArgs e)
{
if (!e.IsGestureRecognitionSkipped)
if (_gestureRecognizers?.HandlePointerMoved(e) == true)
{
e.Handled = true;
}
}
/// <summary>
@ -619,7 +639,7 @@ namespace Avalonia.Input
/// <param name="e">The event args.</param>
protected virtual void OnPointerCaptureLost(PointerCaptureLostEventArgs e)
{
_gestureRecognizers?.HandlePointerCaptureLost(e);
}
/// <summary>

36
src/Avalonia.Base/Input/MouseDevice.cs

@ -163,7 +163,22 @@ namespace Avalonia.Input
device = device ?? throw new ArgumentNullException(nameof(device));
root = root ?? throw new ArgumentNullException(nameof(root));
var source = _pointer.Captured ?? hitTest;
IInputElement source;
if (_pointer.CapturedGestureRecognizer is { } gestureRecognizer)
{
source = gestureRecognizer.Target ?? hitTest;
if(source != null)
{
var e = new PointerEventArgs(InputElement.PointerMovedEvent, source, _pointer, (Visual)root,
p, timestamp, properties, inputModifiers, intermediatePoints);
gestureRecognizer.PointerMoved(e);
return e.Handled;
}
}
source = _pointer.Captured ?? hitTest;
if (source is object)
{
@ -174,6 +189,7 @@ namespace Avalonia.Input
return e.Handled;
}
return false;
}
@ -183,7 +199,22 @@ namespace Avalonia.Input
device = device ?? throw new ArgumentNullException(nameof(device));
root = root ?? throw new ArgumentNullException(nameof(root));
var source = _pointer.Captured ?? hitTest;
IInputElement source;
if (_pointer.CapturedGestureRecognizer is { } gestureRecognizer)
{
source = gestureRecognizer.Target ?? hitTest;
if (source != null)
{
var e = new PointerReleasedEventArgs(source, _pointer, (Visual)root, p, timestamp, props, inputModifiers,
_lastMouseDownButton);
gestureRecognizer.PointerReleased(e);
return e.Handled;
}
}
source = _pointer.Captured ?? hitTest;
if (source is not null)
{
@ -192,6 +223,7 @@ namespace Avalonia.Input
source?.RaiseEvent(e);
_pointer.Capture(null);
_pointer.CaptureGestureRecognizer(null);
_lastMouseDownButton = default;
return e.Handled;
}

35
src/Avalonia.Base/Input/PenDevice.cs

@ -114,7 +114,22 @@ namespace Avalonia.Input
KeyModifiers inputModifiers, IInputElement? hitTest,
Lazy<IReadOnlyList<RawPointerPoint>?>? intermediatePoints)
{
var source = pointer.Captured ?? hitTest;
IInputElement source;
if (pointer.CapturedGestureRecognizer is { } gestureRecognizer)
{
source = gestureRecognizer.Target ?? hitTest;
if (source != null)
{
var e = new PointerEventArgs(InputElement.PointerMovedEvent, source, pointer, (Visual)root,
p, timestamp, properties, inputModifiers, intermediatePoints);
gestureRecognizer.PointerMoved(e);
return e.Handled;
}
}
source = pointer.Captured ?? hitTest;
if (source is not null)
{
@ -132,7 +147,22 @@ namespace Avalonia.Input
IInputElement root, Point p, PointerPointProperties properties,
KeyModifiers inputModifiers, IInputElement? hitTest)
{
var source = pointer.Captured ?? hitTest;
IInputElement source;
if (pointer.CapturedGestureRecognizer is { } gestureRecognizer)
{
source = gestureRecognizer.Target ?? hitTest;
if (source != null)
{
var e = new PointerReleasedEventArgs(source, pointer, (Visual)root, p, timestamp, properties, inputModifiers,
_lastMouseDownButton);
gestureRecognizer.PointerReleased(e);
return e.Handled;
}
}
source = pointer.Captured ?? hitTest;
if (source is not null)
{
@ -141,6 +171,7 @@ namespace Avalonia.Input
source.RaiseEvent(e);
pointer.Capture(null);
pointer.CaptureGestureRecognizer(null);
_lastMouseDownButton = default;
return e.Handled;
}

5
src/Avalonia.Base/Input/Pointer.cs

@ -54,7 +54,7 @@ namespace Avalonia.Input
if (Captured is Visual v3)
v3.DetachedFromVisualTree += OnCaptureDetached;
if (Captured == null)
if (Captured != null)
CaptureGestureRecognizer(null);
}
@ -94,6 +94,9 @@ namespace Avalonia.Input
if (CapturedGestureRecognizer != gestureRecognizer)
CapturedGestureRecognizer?.PointerCaptureLost(this);
if (gestureRecognizer != null)
Capture(null);
CapturedGestureRecognizer = gestureRecognizer;
}
}

10
src/Avalonia.Base/Input/PointerEventArgs.cs

@ -58,6 +58,8 @@ namespace Avalonia.Input
/// </summary>
public ulong Timestamp { get; }
internal bool IsGestureRecognitionSkipped { get; private set; }
/// <summary>
/// Gets a value that indicates which key modifiers were active at the time that the pointer event was initiated.
/// </summary>
@ -121,6 +123,14 @@ namespace Avalonia.Input
return points;
}
/// <summary>
/// Prevents this event from being handled by other gesture recognizers in the route
/// </summary>
public void PreventGestureRecognition()
{
IsGestureRecognitionSkipped = true;
}
/// <summary>
/// Returns the current pointer point properties
/// </summary>

35
src/Avalonia.Base/Input/TouchDevice.cs

@ -52,6 +52,7 @@ namespace Avalonia.Input
}
var target = pointer.Captured ?? args.Root;
var gestureTarget = pointer.CapturedGestureRecognizer?.Target;
var updateKind = args.Type.ToUpdateKind();
var keyModifier = args.InputModifiers.ToKeyModifiers();
@ -95,10 +96,19 @@ namespace Avalonia.Input
_pointers.Remove(args.RawPointerId);
using (pointer)
{
target.RaiseEvent(new PointerReleasedEventArgs(target, pointer,
(Visual)args.Root, args.Position, ev.Timestamp,
new PointerPointProperties(GetModifiers(args.InputModifiers, false), updateKind),
keyModifier, MouseButton.Left));
target = gestureTarget ?? target;
var e = new PointerReleasedEventArgs(target, pointer,
(Visual)args.Root, args.Position, ev.Timestamp,
new PointerPointProperties(GetModifiers(args.InputModifiers, false), updateKind),
keyModifier, MouseButton.Left);
if (gestureTarget != null)
{
pointer?.CapturedGestureRecognizer?.PointerReleased(e);
}
else
{
target.RaiseEvent(e);
}
}
}
@ -106,15 +116,28 @@ namespace Avalonia.Input
{
_pointers.Remove(args.RawPointerId);
using (pointer)
{
pointer.Capture(null);
pointer.CaptureGestureRecognizer(null);
}
}
if (args.Type == RawPointerEventType.TouchUpdate)
{
target.RaiseEvent(new PointerEventArgs(InputElement.PointerMovedEvent, target, pointer, (Visual)args.Root,
target = gestureTarget ?? target;
var e = new PointerEventArgs(InputElement.PointerMovedEvent, target, pointer, (Visual)args.Root,
args.Position, ev.Timestamp,
new PointerPointProperties(GetModifiers(args.InputModifiers, true), updateKind),
keyModifier, args.IntermediatePoints));
keyModifier, args.IntermediatePoints);
if (gestureTarget != null)
{
pointer?.CapturedGestureRecognizer?.PointerMoved(e);
}
else
{
target.RaiseEvent(e);
}
}
}

2
src/Avalonia.Controls/Primitives/Thumb.cs

@ -113,6 +113,8 @@ namespace Avalonia.Controls.Primitives
PseudoClasses.Add(":pressed");
e.PreventGestureRecognition();
RaiseEvent(ev);
}

Loading…
Cancel
Save