inset_embedder/pointer.rs
1//! Flutter counterpart: `engine/src/flutter/lib/ui/pointer.dart`.
2
3use std::fmt::{self, Debug, Display};
4use std::time::Duration;
5
6use crate::ViewId;
7
8/// How the pointer has changed since the last report.
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
10pub enum PointerChange {
11 /// The input from the pointer is no longer directed towards this receiver.
12 Cancel,
13
14 /// The device has started tracking the pointer.
15 ///
16 /// For example, the pointer might be hovering above the device, having not yet
17 /// made contact with the surface of the device.
18 Add,
19
20 /// The device is no longer tracking the pointer.
21 ///
22 /// For example, the pointer might have drifted out of the device's hover
23 /// detection range or might have been disconnected from the system entirely.
24 Remove,
25
26 /// The pointer has moved with respect to the device while not in contact with
27 /// the device.
28 Hover,
29
30 /// The pointer has made contact with the device.
31 Down,
32
33 /// The pointer has moved with respect to the device while in contact with the
34 /// device.
35 Move,
36
37 /// The pointer has stopped making contact with the device.
38 Up,
39
40 /// A pan/zoom has started on this pointer.
41 ///
42 /// This type of event will always have kind [`PointerDeviceKind::Trackpad`].
43 PanZoomStart,
44
45 /// The pan/zoom on this pointer has updated.
46 ///
47 /// This type of event will always have kind [`PointerDeviceKind::Trackpad`].
48 PanZoomUpdate,
49
50 /// The pan/zoom on this pointer has ended.
51 ///
52 /// This type of event will always have kind [`PointerDeviceKind::Trackpad`].
53 PanZoomEnd,
54}
55
56/// The kind of pointer device.
57#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
58pub enum PointerDeviceKind {
59 /// A touch-based pointer device.
60 ///
61 /// The most common case is a touch screen.
62 ///
63 /// When the user is operating with a trackpad on iOS, clicking will also
64 /// dispatch events with kind [`Touch`](PointerDeviceKind::Touch) if
65 /// `UIApplicationSupportsIndirectInputEvents` is not present in `Info.plist`
66 /// or returns NO.
67 ///
68 /// See also:
69 ///
70 /// * [UIApplicationSupportsIndirectInputEvents](https://developer.apple.com/documentation/bundleresources/information_property_list/uiapplicationsupportsindirectinputevents?language=objc).
71 Touch,
72
73 /// A mouse-based pointer device.
74 ///
75 /// The most common case is a mouse on the desktop or Web.
76 ///
77 /// When the user is operating with a trackpad on iOS, moving the pointing
78 /// cursor will also dispatch events with kind [`Mouse`](PointerDeviceKind::Mouse), and clicking will
79 /// dispatch events with kind [`Mouse`](PointerDeviceKind::Mouse) if
80 /// `UIApplicationSupportsIndirectInputEvents` is not present in `Info.plist`
81 /// or returns NO.
82 ///
83 /// See also:
84 ///
85 /// * [UIApplicationSupportsIndirectInputEvents](https://developer.apple.com/documentation/bundleresources/information_property_list/uiapplicationsupportsindirectinputevents?language=objc).
86 Mouse,
87
88 /// A pointer device with a stylus.
89 Stylus,
90
91 /// A pointer device with a stylus that has been inverted.
92 InvertedStylus,
93
94 /// Gestures from a trackpad.
95 ///
96 /// A trackpad here is defined as a touch-based pointer device with an
97 /// indirect surface (the user operates the screen by touching something that
98 /// is not the screen).
99 ///
100 /// When the user makes zoom, pan, scroll or rotate gestures with a physical
101 /// trackpad, supporting platforms dispatch events with kind [`Trackpad`](PointerDeviceKind::Trackpad).
102 ///
103 /// Events with kind [`Trackpad`](PointerDeviceKind::Trackpad) can only have a [`PointerChange`] of `Add`,
104 /// `Remove`, and pan-zoom related values.
105 ///
106 /// Some platforms don't support (or don't fully support) trackpad
107 /// gestures, and might convert trackpad gestures into fake pointer events
108 /// that simulate dragging. These events typically have kind [`Touch`](PointerDeviceKind::Touch) or
109 /// [`Mouse`](PointerDeviceKind::Mouse) instead of [`Trackpad`](PointerDeviceKind::Trackpad). This includes (but is not limited to) Web,
110 /// and iOS when `UIApplicationSupportsIndirectInputEvents` isn't present in
111 /// `Info.plist` or returns NO.
112 ///
113 /// Moving the pointing cursor or clicking with a trackpad typically triggers
114 /// [`Touch`](PointerDeviceKind::Touch) or [`Mouse`](PointerDeviceKind::Mouse) events, but never triggers [`Trackpad`](PointerDeviceKind::Trackpad) events.
115 ///
116 /// See also:
117 ///
118 /// * [UIApplicationSupportsIndirectInputEvents](https://developer.apple.com/documentation/bundleresources/information_property_list/uiapplicationsupportsindirectinputevents?language=objc).
119 Trackpad,
120
121 /// An unknown pointer device.
122 Unknown,
123}
124
125/// The kind of pointer signal event.
126#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
127pub enum PointerSignalKind {
128 /// The event is not associated with a pointer signal.
129 None,
130
131 /// A pointer-generated scroll (e.g., mouse wheel or trackpad scroll).
132 Scroll,
133
134 /// A pointer-generated scroll-inertia cancel.
135 ScrollInertiaCancel,
136
137 /// A pointer-generated scale event (e.g. trackpad pinch).
138 Scale,
139
140 /// An unknown pointer signal kind.
141 Unknown,
142}
143
144/// Information about the state of a pointer.
145#[derive(Clone, Copy, Debug)]
146pub struct PointerData {
147 /// The ID of the [`crate::View`] this pointer event originated from.
148 pub view_id: ViewId,
149
150 /// Unique identifier that ties the pointer event to the embedder
151 /// event that created it.
152 ///
153 /// No two pointer events can have the same [`embedder_id`](Self::embedder_id). This is different
154 /// from [`pointer_identifier`](Self::pointer_identifier) - used for hit-testing, whereas [`embedder_id`](Self::embedder_id) is
155 /// used to identify the platform event.
156 pub embedder_id: i64,
157
158 /// Time of event dispatch, relative to an arbitrary timeline.
159 pub time_stamp: Duration,
160
161 /// How the pointer has changed since the last report.
162 pub change: PointerChange,
163
164 /// The kind of input device for which the event was generated.
165 pub kind: PointerDeviceKind,
166
167 /// The kind of signal for a pointer signal event.
168 pub signal_kind: Option<PointerSignalKind>,
169
170 /// Unique identifier for the pointing device, reused across interactions.
171 pub device: i64,
172
173 /// Unique identifier for the pointer.
174 ///
175 /// This field changes for each new pointer down event. Framework uses this
176 /// identifier to determine hit test result.
177 pub pointer_identifier: i64,
178
179 /// X coordinate of the position of the pointer, in physical pixels in the
180 /// global coordinate space.
181 pub physical_x: f64,
182
183 /// Y coordinate of the position of the pointer, in physical pixels in the
184 /// global coordinate space.
185 pub physical_y: f64,
186
187 /// The distance of pointer movement on X coordinate in physical pixels.
188 pub physical_delta_x: f64,
189
190 /// The distance of pointer movement on Y coordinate in physical pixels.
191 pub physical_delta_y: f64,
192
193 /// Bit field using the *Button constants (primaryMouseButton,
194 /// secondaryStylusButton, etc). For example, if this has the value 6 and the
195 /// [`kind`](Self::kind) is [`PointerDeviceKind::InvertedStylus`], then this indicates an
196 /// upside-down stylus with both its primary and secondary buttons pressed.
197 pub buttons: i64,
198
199 /// Set if an application from a different security domain is in any way
200 /// obscuring this application's window. (Aspirational; not currently
201 /// implemented.)
202 pub obscured: bool,
203
204 /// Set if this pointer data was synthesized by pointer data packet converter.
205 /// pointer data packet converter will synthesize additional pointer datas if
206 /// the input sequence of pointer data is illegal.
207 ///
208 /// For example, a down pointer data will be synthesized if the converter receives
209 /// a move pointer data while the pointer is not previously down.
210 pub synthesized: bool,
211
212 /// The pressure of the touch as a number ranging from 0.0, indicating a touch
213 /// with no discernible pressure, to 1.0, indicating a touch with "normal"
214 /// pressure, and possibly beyond, indicating a stronger touch. For devices
215 /// that do not detect pressure (e.g. mice), returns 1.0.
216 pub pressure: f64,
217
218 /// The minimum value that [`pressure`](Self::pressure) can return for this pointer. For devices
219 /// that do not detect pressure (e.g. mice), returns 1.0. This will always be
220 /// a number less than or equal to 1.0.
221 pub pressure_min: f64,
222
223 /// The maximum value that [`pressure`](Self::pressure) can return for this pointer. For devices
224 /// that do not detect pressure (e.g. mice), returns 1.0. This will always be
225 /// a greater than or equal to 1.0.
226 pub pressure_max: f64,
227
228 /// The distance of the detected object from the input surface (e.g. the
229 /// distance of a stylus or finger from a touch screen), in arbitrary units on
230 /// an arbitrary (not necessarily linear) scale. If the pointer is down, this
231 /// is 0.0 by definition.
232 pub distance: f64,
233
234 /// The maximum value that a distance can return for this pointer. If this
235 /// input device cannot detect "hover touch" input events, then this will be
236 /// 0.0.
237 pub distance_max: f64,
238
239 /// The area of the screen being pressed, scaled to a value between 0 and 1.
240 /// The value of size can be used to determine fat touch events. This value
241 /// is only set on Android, and is a device specific approximation within
242 /// the range of detectable values. So, for example, the value of 0.1 could
243 /// mean a touch with the tip of the finger, 0.2 a touch with full finger,
244 /// and 0.3 the full palm.
245 pub size: f64,
246
247 /// The radius of the contact ellipse along the major axis, in logical pixels.
248 pub radius_major: f64,
249
250 /// The radius of the contact ellipse along the minor axis, in logical pixels.
251 pub radius_minor: f64,
252
253 /// The minimum value that could be reported for radiusMajor and radiusMinor
254 /// for this pointer, in logical pixels.
255 pub radius_min: f64,
256
257 /// The maximum value that could be reported for radiusMajor and radiusMinor
258 /// for this pointer, in logical pixels.
259 pub radius_max: f64,
260
261 /// For [`PointerDeviceKind::Touch`] events:
262 ///
263 /// The angle of the contact ellipse, in radius in the range:
264 ///
265 /// -pi/2 < orientation <= pi/2
266 ///
267 /// ...giving the angle of the major axis of the ellipse with the y-axis
268 /// (negative angles indicating an orientation along the top-left /
269 /// bottom-right diagonal, positive angles indicating an orientation along the
270 /// top-right / bottom-left diagonal, and zero indicating an orientation
271 /// parallel with the y-axis).
272 ///
273 /// For [`PointerDeviceKind::Stylus`] and [`PointerDeviceKind::InvertedStylus`] events:
274 ///
275 /// The angle of the stylus, in radians in the range:
276 ///
277 /// -pi < orientation <= pi
278 ///
279 /// ...giving the angle of the axis of the stylus projected onto the input
280 /// surface, relative to the positive y-axis of that surface (thus 0.0
281 /// indicates the stylus, if projected onto that surface, would go from the
282 /// contact point vertically up in the positive y-axis direction, pi would
283 /// indicate that the stylus would go down in the negative y-axis direction;
284 /// pi/4 would indicate that the stylus goes up and to the right, -pi/2 would
285 /// indicate that the stylus goes to the left, etc).
286 pub orientation: f64,
287
288 /// For [`PointerDeviceKind::Stylus`] and [`PointerDeviceKind::InvertedStylus`] events:
289 ///
290 /// The angle of the stylus, in radians in the range:
291 ///
292 /// 0 <= tilt <= pi/2
293 ///
294 /// ...giving the angle of the axis of the stylus, relative to the axis
295 /// perpendicular to the input surface (thus 0.0 indicates the stylus is
296 /// orthogonal to the plane of the input surface, while pi/2 indicates that
297 /// the stylus is flat on that surface).
298 pub tilt: f64,
299
300 /// Opaque platform-specific data associated with the event.
301 pub platform_data: i64,
302
303 /// For events with signalKind of [`PointerSignalKind::Scroll`]:
304 ///
305 /// The amount to scroll in the x direction, in physical pixels.
306 pub scroll_delta_x: f64,
307
308 /// For events with signalKind of [`PointerSignalKind::Scroll`]:
309 ///
310 /// The amount to scroll in the y direction, in physical pixels.
311 pub scroll_delta_y: f64,
312
313 /// For events with change of [`PointerChange::PanZoomUpdate`]:
314 ///
315 /// The current panning magnitude of the pan/zoom in the x direction, in
316 /// physical pixels.
317 pub pan_x: f64,
318
319 /// For events with change of [`PointerChange::PanZoomUpdate`]:
320 ///
321 /// The current panning magnitude of the pan/zoom in the y direction, in
322 /// physical pixels.
323 pub pan_y: f64,
324
325 /// For events with change of [`PointerChange::PanZoomUpdate`]:
326 ///
327 /// The difference in panning of the pan/zoom in the x direction since the
328 /// latest panZoomUpdate event, in physical pixels.
329 pub pan_delta_x: f64,
330
331 /// For events with change of [`PointerChange::PanZoomUpdate`]:
332 ///
333 /// The difference in panning of the pan/zoom in the y direction since the
334 /// last panZoomUpdate event, in physical pixels.
335 pub pan_delta_y: f64,
336
337 /// For events with change of [`PointerChange::PanZoomUpdate`]:
338 ///
339 /// The current scale of the pan/zoom (unitless), with 1.0 as the initial scale.
340 pub scale: f64,
341
342 /// For events with change of [`PointerChange::PanZoomUpdate`]:
343 ///
344 /// The current angle of the pan/zoom in radians, with 0.0 as the initial angle.
345 pub rotation: f64,
346}
347
348impl Default for PointerData {
349 fn default() -> PointerData {
350 PointerData {
351 view_id: ViewId(0),
352 embedder_id: 0,
353 time_stamp: Duration::ZERO,
354 change: PointerChange::Cancel,
355 kind: PointerDeviceKind::Touch,
356 signal_kind: None,
357 device: 0,
358 pointer_identifier: 0,
359 physical_x: 0.0,
360 physical_y: 0.0,
361 physical_delta_x: 0.0,
362 physical_delta_y: 0.0,
363 buttons: 0,
364 obscured: false,
365 synthesized: false,
366 pressure: 0.0,
367 pressure_min: 0.0,
368 pressure_max: 0.0,
369 distance: 0.0,
370 distance_max: 0.0,
371 size: 0.0,
372 radius_major: 0.0,
373 radius_minor: 0.0,
374 radius_min: 0.0,
375 radius_max: 0.0,
376 orientation: 0.0,
377 tilt: 0.0,
378 platform_data: 0,
379 scroll_delta_x: 0.0,
380 scroll_delta_y: 0.0,
381 pan_x: 0.0,
382 pan_y: 0.0,
383 pan_delta_x: 0.0,
384 pan_delta_y: 0.0,
385 scale: 0.0,
386 rotation: 0.0,
387 }
388 }
389}
390
391impl PointerData {
392 /// Returns a complete textual description of the information in this object.
393 pub fn to_string_full(&self) -> String {
394 format!(
395 "PointerData(\
396 embedderId: {}, \
397 timeStamp: {:?}, \
398 change: {:?}, \
399 kind: {:?}, \
400 signalKind: {:?}, \
401 device: {}, \
402 pointerIdentifier: {}, \
403 physicalX: {}, \
404 physicalY: {}, \
405 physicalDeltaX: {}, \
406 physicalDeltaY: {}, \
407 buttons: {}, \
408 synthesized: {}, \
409 pressure: {}, \
410 pressureMin: {}, \
411 pressureMax: {}, \
412 distance: {}, \
413 distanceMax: {}, \
414 size: {}, \
415 radiusMajor: {}, \
416 radiusMinor: {}, \
417 radiusMin: {}, \
418 radiusMax: {}, \
419 orientation: {}, \
420 tilt: {}, \
421 platformData: {}, \
422 scrollDeltaX: {}, \
423 scrollDeltaY: {}, \
424 panX: {}, \
425 panY: {}, \
426 panDeltaX: {}, \
427 panDeltaY: {}, \
428 scale: {}, \
429 rotation: {}, \
430 viewId: {:?}\
431 )",
432 self.embedder_id,
433 self.time_stamp,
434 self.change,
435 self.kind,
436 self.signal_kind,
437 self.device,
438 self.pointer_identifier,
439 self.physical_x,
440 self.physical_y,
441 self.physical_delta_x,
442 self.physical_delta_y,
443 self.buttons,
444 self.synthesized,
445 self.pressure,
446 self.pressure_min,
447 self.pressure_max,
448 self.distance,
449 self.distance_max,
450 self.size,
451 self.radius_major,
452 self.radius_minor,
453 self.radius_min,
454 self.radius_max,
455 self.orientation,
456 self.tilt,
457 self.platform_data,
458 self.scroll_delta_x,
459 self.scroll_delta_y,
460 self.pan_x,
461 self.pan_y,
462 self.pan_delta_x,
463 self.pan_delta_y,
464 self.scale,
465 self.rotation,
466 self.view_id,
467 )
468 }
469}
470
471impl Display for PointerData {
472 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
473 write!(
474 f,
475 "PointerData(viewId: {}, x: {}, y: {})",
476 self.view_id.0, self.physical_x, self.physical_y
477 )
478 }
479}
480
481/// A sequence of reports about the state of pointers.
482#[derive(Clone, Debug, Default)]
483pub struct PointerDataPacket {
484 /// Data about the individual pointers in this packet.
485 ///
486 /// This list might contain multiple pieces of data about the same pointer.
487 pub data: Vec<PointerData>,
488}
489
490impl PointerDataPacket {
491 /// Creates a packet of pointer data reports.
492 pub fn new(data: Vec<PointerData>) -> PointerDataPacket {
493 PointerDataPacket { data }
494 }
495}
496
497#[cfg(test)]
498mod tests {
499 use super::*;
500
501 #[test]
502 fn pointer_data_defaults_match_dart() {
503 let data = PointerData::default();
504 assert_eq!(data.view_id, ViewId(0));
505 assert_eq!(data.change, PointerChange::Cancel);
506 assert_eq!(data.kind, PointerDeviceKind::Touch);
507 assert!(data.signal_kind.is_none());
508 assert_eq!(data.time_stamp, Duration::ZERO);
509 }
510
511 #[test]
512 fn display_matches_dart_to_string() {
513 let data = PointerData {
514 view_id: ViewId(2),
515 physical_x: 10.0,
516 physical_y: 20.0,
517 ..PointerData::default()
518 };
519 assert_eq!(format!("{data}"), "PointerData(viewId: 2, x: 10, y: 20)");
520 }
521}