10 changed files with 257 additions and 13 deletions
@ -0,0 +1,60 @@ |
|||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using Avalonia.VisualTree; |
||||
|
|
||||
|
namespace Avalonia.Input |
||||
|
{ |
||||
|
public class Pointer : IPointer, IDisposable |
||||
|
{ |
||||
|
public Pointer(int id, PointerType type, bool isPrimary, IInputElement implicitlyCaptured) |
||||
|
{ |
||||
|
Id = id; |
||||
|
Type = type; |
||||
|
IsPrimary = isPrimary; |
||||
|
ImplicitlyCaptured = implicitlyCaptured; |
||||
|
if (ImplicitlyCaptured != null) |
||||
|
ImplicitlyCaptured.DetachedFromVisualTree += OnImplicitCaptureDetached; |
||||
|
} |
||||
|
|
||||
|
public int Id { get; } |
||||
|
|
||||
|
public void Capture(IInputElement control) |
||||
|
{ |
||||
|
if (Captured != null) |
||||
|
Captured.DetachedFromVisualTree -= OnCaptureDetached; |
||||
|
Captured = control; |
||||
|
if (Captured != null) |
||||
|
Captured.DetachedFromVisualTree += OnCaptureDetached; |
||||
|
} |
||||
|
|
||||
|
IInputElement GetNextCapture(IVisual parent) => |
||||
|
parent as IInputElement ?? parent.GetVisualAncestors().OfType<IInputElement>().FirstOrDefault(); |
||||
|
|
||||
|
private void OnCaptureDetached(object sender, VisualTreeAttachmentEventArgs e) |
||||
|
{ |
||||
|
Capture(GetNextCapture(e.Parent)); |
||||
|
} |
||||
|
|
||||
|
private void OnImplicitCaptureDetached(object sender, VisualTreeAttachmentEventArgs e) |
||||
|
{ |
||||
|
ImplicitlyCaptured.DetachedFromVisualTree -= OnImplicitCaptureDetached; |
||||
|
ImplicitlyCaptured = GetNextCapture(e.Parent); |
||||
|
if (ImplicitlyCaptured != null) |
||||
|
ImplicitlyCaptured.DetachedFromVisualTree += OnImplicitCaptureDetached; |
||||
|
} |
||||
|
|
||||
|
public IInputElement Captured { get; private set; } |
||||
|
public IInputElement ImplicitlyCaptured { get; private set; } |
||||
|
public IInputElement GetEffectiveCapture() => Captured ?? ImplicitlyCaptured; |
||||
|
|
||||
|
public PointerType Type { get; } |
||||
|
public bool IsPrimary { get; } |
||||
|
public void Dispose() |
||||
|
{ |
||||
|
if (ImplicitlyCaptured != null) |
||||
|
ImplicitlyCaptured.DetachedFromVisualTree -= OnImplicitCaptureDetached; |
||||
|
if (Captured != null) |
||||
|
Captured.DetachedFromVisualTree -= OnCaptureDetached; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,70 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using Avalonia.Input.Raw; |
||||
|
using Avalonia.VisualTree; |
||||
|
|
||||
|
namespace Avalonia.Input |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Handles raw touch events
|
||||
|
/// This class is supposed to be used on per-toplevel basis, don't event try to have a global instance
|
||||
|
/// </summary>
|
||||
|
public class TouchDevice : IInputDevice |
||||
|
{ |
||||
|
Dictionary<long, Pointer> _pointers = new Dictionary<long, Pointer>(); |
||||
|
|
||||
|
static InputModifiers GetModifiers(InputModifiers modifiers, bool left) |
||||
|
{ |
||||
|
var mask = (InputModifiers)0x7fffffff ^ InputModifiers.LeftMouseButton ^ InputModifiers.MiddleMouseButton ^ |
||||
|
InputModifiers.RightMouseButton; |
||||
|
modifiers &= mask; |
||||
|
if (left) |
||||
|
modifiers |= InputModifiers.LeftMouseButton; |
||||
|
return modifiers; |
||||
|
} |
||||
|
|
||||
|
public void ProcessRawEvent(RawInputEventArgs ev) |
||||
|
{ |
||||
|
var args = (RawTouchEventArgs)ev; |
||||
|
if (!_pointers.TryGetValue(args.TouchPointId, out var pointer)) |
||||
|
{ |
||||
|
if (args.Type == RawMouseEventType.TouchEnd) |
||||
|
return; |
||||
|
var hit = args.Root.InputHitTest(args.Position); |
||||
|
|
||||
|
_pointers[args.TouchPointId] = pointer = new Pointer(PointerIds.Next(), |
||||
|
PointerType.Touch, _pointers.Count == 0, hit); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
var target = pointer.GetEffectiveCapture() ?? args.Root; |
||||
|
if (args.Type == RawMouseEventType.TouchBegin) |
||||
|
{ |
||||
|
var modifiers = GetModifiers(args.InputModifiers, pointer.IsPrimary); |
||||
|
target.RaiseEvent(new PointerPressedEventArgs(target, pointer, |
||||
|
args.Root, args.Position, new PointerPointProperties(modifiers), |
||||
|
modifiers)); |
||||
|
} |
||||
|
|
||||
|
if (args.Type == RawMouseEventType.TouchEnd) |
||||
|
{ |
||||
|
_pointers.Remove(args.TouchPointId); |
||||
|
var modifiers = GetModifiers(args.InputModifiers, false); |
||||
|
using (pointer) |
||||
|
{ |
||||
|
target.RaiseEvent(new PointerReleasedEventArgs(target, pointer, |
||||
|
args.Root, args.Position, new PointerPointProperties(modifiers), |
||||
|
modifiers, pointer.IsPrimary ? MouseButton.Left : MouseButton.None)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (args.Type == RawMouseEventType.TouchUpdate) |
||||
|
{ |
||||
|
var modifiers = GetModifiers(args.InputModifiers, pointer.IsPrimary); |
||||
|
target.RaiseEvent(new PointerEventArgs(InputElement.PointerMovedEvent, target, pointer, args.Root, |
||||
|
args.Position, new PointerPointProperties(modifiers), modifiers)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,60 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Interactivity; |
||||
|
|
||||
|
namespace Avalonia.Input.TouchGestureRecognizers |
||||
|
{ |
||||
|
/* |
||||
|
public class TapGestureRecognizer : ITouchGestureRecognizer |
||||
|
{ |
||||
|
long _started; |
||||
|
Point _startPoint; |
||||
|
const double Distance = 20; |
||||
|
const long MaxTapDuration = 500; |
||||
|
TouchGestureRecognizerResult ITouchGestureRecognizer.RecognizeGesture(IInputElement owner, TouchEventArgs args) |
||||
|
{ |
||||
|
if (args.Route == RoutingStrategies.Tunnel) |
||||
|
return TouchGestureRecognizerResult.Continue; |
||||
|
|
||||
|
// Multi-touch sequence
|
||||
|
if(args.Touches.Count > 1) |
||||
|
return TouchGestureRecognizerResult.Reject; |
||||
|
// Sequence started, save the start time
|
||||
|
if(args.Type == TouchEventType.TouchBegin) |
||||
|
{ |
||||
|
_started = args.Timestamp; |
||||
|
var pos = args.Touches[0].GetPosition(owner); |
||||
|
if (pos == null) |
||||
|
return TouchGestureRecognizerResult.Reject; |
||||
|
_startPoint = pos.Value; |
||||
|
return TouchGestureRecognizerResult.Continue; |
||||
|
} |
||||
|
|
||||
|
if(args.Type == TouchEventType.TouchEnd) |
||||
|
{ |
||||
|
var pos = args.RemovedTouches[0].GetPosition(owner); |
||||
|
if (pos == null) |
||||
|
return TouchGestureRecognizerResult.Reject; |
||||
|
var endPoint = pos.Value; |
||||
|
|
||||
|
if(Math.Abs(endPoint.X - _startPoint.X) < Distance |
||||
|
&& Math.Abs(endPoint.Y - _startPoint.Y) < Distance |
||||
|
&& (args.Timestamp - _started) < MaxTapDuration) |
||||
|
{ |
||||
|
((Interactive)args.RemovedTouches[0].InitialTarget).RaiseEvent( |
||||
|
new RoutedEventArgs(Gestures.TappedEvent)); |
||||
|
return TouchGestureRecognizerResult.Accept; |
||||
|
} |
||||
|
else |
||||
|
return TouchGestureRecognizerResult.Reject; |
||||
|
} |
||||
|
return TouchGestureRecognizerResult.Continue; |
||||
|
} |
||||
|
|
||||
|
public void Cancel() |
||||
|
{ |
||||
|
_started = 0; |
||||
|
_startPoint = default; |
||||
|
} |
||||
|
} |
||||
|
*/ |
||||
|
} |
||||
Loading…
Reference in new issue