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