-
Notifications
You must be signed in to change notification settings - Fork 717
/
PointerRoutedEventArgs.iOS.cs
139 lines (115 loc) · 4.51 KB
/
PointerRoutedEventArgs.iOS.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
using System;
using System.Collections.Generic;
using System.Text;
using Windows.Foundation;
using Windows.System;
using Foundation;
using UIKit;
using Uno.UI.Xaml.Core;
using Uno.UI.Xaml.Input;
#if HAS_UNO_WINUI
using Microsoft.UI.Input;
#else
using Windows.UI.Input;
using Windows.Devices.Input;
#endif
namespace Microsoft.UI.Xaml.Input
{
partial class PointerRoutedEventArgs
{
private readonly UITouch _nativeTouch;
private readonly UIEvent _nativeEvent;
private readonly PointerPointProperties _properties;
/// <summary>
/// Creates an hybrid event args which reports the <paramref name="current"/> position, time and original source,
/// while reporting the state of the <paramref name="previous"/> args (pressed buttons, key modifiers, etc.).
/// </summary>
/// <remarks>
/// This has a very specific usage and should be used cautiously!
/// </remarks>
internal PointerRoutedEventArgs(PointerRoutedEventArgs previous, PointerRoutedEventArgs current)
{
_nativeTouch = current._nativeTouch;
_nativeEvent = current._nativeEvent;
FrameId = current.FrameId;
Pointer = previous.Pointer;
KeyModifiers = previous.KeyModifiers;
OriginalSource = current.OriginalSource;
_properties = previous._properties;
}
internal PointerRoutedEventArgs(uint pointerId, UITouch nativeTouch, UIEvent nativeEvent, UIElement originalSource) : this()
{
_nativeTouch = nativeTouch;
_nativeEvent = nativeEvent;
var deviceType = GetPointerDeviceType(nativeTouch.Type);
var isInContact = _nativeTouch.Phase == UITouchPhase.Began
|| _nativeTouch.Phase == UITouchPhase.Moved
|| _nativeTouch.Phase == UITouchPhase.Stationary;
FrameId = ToFrameId(_nativeTouch.Timestamp);
Pointer = new Pointer(pointerId, deviceType, isInContact, isInRange: true);
KeyModifiers = VirtualKeyModifiers.None;
OriginalSource = originalSource;
var inputManager = VisualTree.GetContentRootForElement(originalSource)?.InputManager;
if (inputManager is not null)
{
inputManager.LastInputDeviceType = deviceType switch
{
PointerDeviceType.Mouse => InputDeviceType.Mouse,
PointerDeviceType.Pen => InputDeviceType.Pen,
_ => InputDeviceType.Touch
};
}
_properties = GetProperties(); // Make sure to capture the properties state so we can re-use them in "mixed" ctor
}
public PointerPoint GetCurrentPoint(UIElement relativeTo)
{
var timestamp = ToTimeStamp(_nativeTouch.Timestamp);
var device = global::Windows.Devices.Input.PointerDevice.For((global::Windows.Devices.Input.PointerDeviceType)Pointer.PointerDeviceType);
var rawPosition = (Point)_nativeTouch.GetPreciseLocation(null);
var position = relativeTo == null
? rawPosition
: (Point)_nativeTouch.GetPreciseLocation(relativeTo);
return new PointerPoint(FrameId, timestamp, device, Pointer.PointerId, rawPosition, position, Pointer.IsInContact, _properties);
}
private PointerDeviceType GetPointerDeviceType(UITouchType touchType) =>
touchType switch
{
UITouchType.Stylus => PointerDeviceType.Pen,
UITouchType.IndirectPointer => PointerDeviceType.Mouse,
UITouchType.Indirect => PointerDeviceType.Mouse,
_ => PointerDeviceType.Touch // Use touch as default fallback.
};
private PointerPointProperties GetProperties()
=> new()
{
IsPrimary = true,
IsInRange = Pointer.IsInRange,
IsLeftButtonPressed = Pointer.IsInContact,
Pressure = (float)(_nativeTouch.Force / _nativeTouch.MaximumPossibleForce),
PointerUpdateKind = _nativeTouch.Phase switch
{
UITouchPhase.Began => PointerUpdateKind.LeftButtonPressed,
UITouchPhase.Ended => PointerUpdateKind.LeftButtonReleased,
_ => PointerUpdateKind.Other
}
};
#region Misc static helpers
private static long? _bootTime;
private static ulong ToTimeStamp(double timestamp)
{
_bootTime ??= DateTime.UtcNow.Ticks - (long)(TimeSpan.TicksPerSecond * new NSProcessInfo().SystemUptime);
return (ulong)_bootTime.Value + (ulong)(TimeSpan.TicksPerSecond * timestamp);
}
private static double? _firstTimestamp;
private static uint ToFrameId(double timestamp)
{
_firstTimestamp ??= timestamp;
var relativeTimestamp = timestamp - _firstTimestamp;
var frameId = relativeTimestamp * 120.0; // we allow a precision of 120Hz (8.333 ms per frame)
// When we cast, we are not overflowing but instead capping to uint.MaxValue.
// We use modulo to make sure to reset to 0 in that case (1.13 years of app run-time, but we prefer to be safe).
return (uint)(frameId % uint.MaxValue);
}
#endregion
}
}