Skip to main content

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}