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)]
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}