zng_ext_input/
touch.rs

1//! Touch events and service.
2//!
3//! The app extension [`TouchManager`] provides the events and service. It is included in the default application.
4
5use std::{collections::HashMap, mem, num::NonZeroU32, ops, time::Duration};
6use zng_app::{
7    AppExtension, DInstant,
8    event::{AnyEventArgs, EventPropagationHandle, event, event_args},
9    shortcut::ModifiersState,
10    timer::{DeadlineVar, TIMERS},
11    update::EventUpdate,
12    view_process::{
13        VIEW_PROCESS_INITED_EVENT,
14        raw_device_events::DeviceId,
15        raw_events::{RAW_FRAME_RENDERED_EVENT, RAW_MOUSE_LEFT_EVENT, RAW_TOUCH_CONFIG_CHANGED_EVENT, RAW_TOUCH_EVENT, RawTouchArgs},
16    },
17    widget::{
18        WIDGET, WidgetId,
19        info::{HitTestInfo, InteractionPath, WIDGET_INFO_CHANGED_EVENT},
20    },
21    window::WindowId,
22};
23
24use zng_app_context::app_local;
25use zng_ext_window::{NestedWindowWidgetInfoExt as _, WINDOWS};
26use zng_layout::unit::{
27    AngleRadian, Dip, DipPoint, DipToPx, DipVector, Factor, Px, PxPoint, PxToDip, PxTransform, PxVector, TimeUnits, euclid,
28};
29use zng_var::{ArcVar, ReadOnlyArcVar, Var, impl_from_and_into_var, types::ArcCowVar, var};
30pub use zng_view_api::{
31    config::TouchConfig,
32    touch::{TouchForce, TouchId, TouchPhase, TouchUpdate},
33};
34
35use crate::{
36    keyboard::MODIFIERS_CHANGED_EVENT,
37    pointer_capture::{CaptureInfo, POINTER_CAPTURE, POINTER_CAPTURE_EVENT},
38};
39
40/// Application extension that provides touch events and service.
41///
42/// # Events
43///
44/// Events this extension provides.
45///
46/// * [`TOUCH_MOVE_EVENT`]
47/// * [`TOUCH_INPUT_EVENT`]
48/// * [`TOUCHED_EVENT`]
49/// * [`TOUCH_TAP_EVENT`]
50/// * [`TOUCH_TRANSFORM_EVENT`]
51/// * [`TOUCH_LONG_PRESS_EVENT`]
52///
53/// # Services
54///
55/// Services this extension provides.
56///
57/// * [`TOUCH`]
58#[derive(Default)]
59pub struct TouchManager {
60    modifiers: ModifiersState,
61    pressed: HashMap<TouchId, PressedInfo>,
62    tap_gesture: TapGesture,
63    transform_gesture: TransformGesture,
64    long_press_gesture: LongPressGesture,
65    mouse_touch: Option<TouchId>,
66}
67struct PressedInfo {
68    touch_propagation: EventPropagationHandle,
69    target: InteractionPath,
70    device_id: DeviceId,
71    position: DipPoint,
72    force: Option<TouchForce>,
73    hits: HitTestInfo,
74    velocity_samples: Vec<(DInstant, DipPoint)>,
75}
76impl PressedInfo {
77    fn push_velocity_sample(&mut self, timestamp: DInstant, position: DipPoint) {
78        if let Some(last) = self.velocity_samples.last_mut() {
79            if timestamp.duration_since(last.0) < 1.ms() {
80                last.1 = position;
81                return;
82            }
83        }
84
85        if self.velocity_samples.len() == 4 {
86            self.velocity_samples.remove(0);
87        }
88        self.velocity_samples.push((timestamp, position));
89    }
90
91    fn velocity(&self) -> DipVector {
92        if self.velocity_samples.len() < 4 {
93            DipVector::zero()
94        } else {
95            let samples = [
96                self.velocity_samples[0].1.cast::<f64>(),
97                self.velocity_samples[1].1.cast(),
98                self.velocity_samples[2].1.cast(),
99                self.velocity_samples[3].1.cast(),
100            ];
101            let velocity_at = |end_i: usize| {
102                let start_i = end_i - 1;
103
104                let start_t = self.velocity_samples[start_i].0;
105                let end_t = self.velocity_samples[end_i].0;
106
107                let start_s = samples[start_i];
108                let end_s = samples[end_i];
109
110                let delta = (end_t - start_t).as_secs_f64();
111
112                if delta > 0.0 {
113                    (end_s - start_s) / delta
114                } else {
115                    euclid::vec2(0.0, 0.0)
116                }
117            };
118
119            let v23 = velocity_at(3) * 0.6;
120            let v12 = velocity_at(2) * 0.35;
121            let v01 = velocity_at(1) * 0.05;
122            let v = v23 + v12 + v01;
123
124            v.cast::<f32>().cast()
125        }
126    }
127}
128
129/// Touch service.
130///
131/// # Touch Capture
132///
133/// Touch capture is integrated with mouse capture in the [`POINTER_CAPTURE`] service.
134///
135/// # Provider
136///
137/// This service is provided by the [`TouchManager`] extension.
138///
139/// [`POINTER_CAPTURE`]: crate::pointer_capture::POINTER_CAPTURE
140pub struct TOUCH;
141
142impl TOUCH {
143    /// Variable that defines the touch config for the app.
144    ///
145    /// The value is the same as [`sys_touch_config`], if set the variable disconnects from system config.
146    ///
147    /// [`sys_touch_config`]: Self::sys_touch_config
148    pub fn touch_config(&self) -> ArcCowVar<TouchConfig, ArcVar<TouchConfig>> {
149        TOUCH_SV.read().touch_config.clone()
150    }
151
152    /// Read-only variable that tracks the system touch config.
153    ///
154    /// Note that some of these configs are not always used, a tap event for example can happen even if the
155    /// touch moves out of the `tap_area` when there is no ambiguity.
156    ///
157    /// # Value Source
158    ///
159    /// The value comes from the operating system settings, the variable
160    /// updates with a new value if the system setting is changed and on view-process (re)init.
161    ///
162    /// In headless apps the default is [`TouchConfig::default`] and does not change.
163    pub fn sys_touch_config(&self) -> ReadOnlyArcVar<TouchConfig> {
164        TOUCH_SV.read().sys_touch_config.read_only()
165    }
166
167    /// Variable that tracks all current active touches.
168    pub fn positions(&self) -> ReadOnlyArcVar<Vec<TouchPosition>> {
169        TOUCH_SV.read().positions.read_only()
170    }
171
172    /// Test mode, generates touch events for a single touch contact from raw mouse events.
173    ///
174    /// Is disabled by default.
175    pub fn touch_from_mouse_events(&self) -> ArcVar<bool> {
176        TOUCH_SV.read().touch_from_mouse_events.clone()
177    }
178}
179
180/// Active touch positions.
181///
182/// Tracked in [`TOUCH.positions`].
183///
184/// [`TOUCH.positions`]: TOUCH::positions
185#[derive(Debug, Clone, PartialEq)]
186pub struct TouchPosition {
187    /// Touched window.
188    pub window_id: WindowId,
189    /// Unique ID of the touch, among other active touches.
190    pub touch: TouchId,
191    /// Latest touch contact position.
192    pub position: DipPoint,
193
194    /// Touch start timestamp.
195    pub start_time: DInstant,
196    /// Latest move timestamp.
197    pub update_time: DInstant,
198}
199
200app_local! {
201    static TOUCH_SV: TouchService = {
202        let sys_touch_config = var(TouchConfig::default());
203        TouchService {
204            touch_config: sys_touch_config.cow(),
205            sys_touch_config,
206            positions: var(vec![]),
207            touch_from_mouse_events: var(false),
208        }
209    };
210}
211struct TouchService {
212    touch_config: ArcCowVar<TouchConfig, ArcVar<TouchConfig>>,
213    sys_touch_config: ArcVar<TouchConfig>,
214    positions: ArcVar<Vec<TouchPosition>>,
215    touch_from_mouse_events: ArcVar<bool>,
216}
217
218/// Identify the moves of one touch contact in [`TouchMoveArgs`].
219#[derive(Debug, Clone)]
220pub struct TouchMove {
221    /// Identify the touch contact or *finger*.
222    ///
223    /// Multiple points of contact can happen in the same device at the same time,
224    /// this ID identifies each uninterrupted contact. IDs are unique only among other concurrent touches
225    /// on the same device, after a touch is ended an ID may be reused.
226    pub touch: TouchId,
227
228    /// Handle across the lifetime of `touch`.
229    ///
230    /// See [`TouchInputArgs::touch_propagation`] for more details.
231    pub touch_propagation: EventPropagationHandle,
232
233    /// Coalesced moves of the touch since last event.
234    ///
235    /// Last entry is the latest position.
236    pub moves: Vec<(DipPoint, Option<TouchForce>)>,
237
238    /// Velocity in device independent pixels per second.
239    ///
240    /// The velocity is computed from the 4 non-coalesced move events. If is zero before the fourth event.
241    pub velocity: DipVector,
242
243    /// Hit-test result for the latest touch point in the window.
244    pub hits: HitTestInfo,
245
246    /// Full path to the top-most hit in [`hits`](TouchMove::hits).
247    pub target: InteractionPath,
248}
249impl TouchMove {
250    /// Latest position.
251    pub fn position(&self) -> DipPoint {
252        self.moves.last().map(|(p, _)| *p).unwrap_or_else(DipPoint::zero)
253    }
254}
255
256event_args! {
257    /// Arguments for [`TOUCH_MOVE_EVENT`].
258    pub struct TouchMoveArgs {
259        /// Id of window that received all touches in this event.
260        pub window_id: WindowId,
261
262        /// Id of device that generated all touches in this event.
263        pub device_id: DeviceId,
264
265        /// All touch contacts that moved since last event.
266        ///
267        /// Note that if a touch contact did not move it will not be in the list, the touch may still be active
268        /// however, the [`TOUCH_INPUT_EVENT`] can be used to track touch start and end.
269        pub touches: Vec<TouchMove>,
270
271        /// Current pointer capture.
272        pub capture: Option<CaptureInfo>,
273
274        /// What modifier keys where pressed when this event happened.
275        pub modifiers: ModifiersState,
276
277        ..
278
279        /// Each [`TouchMove::target`] and [`capture`].
280        ///
281        /// [`capture`]: Self::capture
282        fn delivery_list(&self, list: &mut UpdateDeliveryList) {
283            for t in &self.touches {
284                list.insert_wgt(&t.target);
285            }
286            if let Some(c) = &self.capture {
287                list.insert_wgt(&c.target);
288            }
289        }
290    }
291
292    /// Arguments for [`TOUCH_INPUT_EVENT`].
293    pub struct TouchInputArgs {
294        /// Id of window that received the event.
295        pub window_id: WindowId,
296
297        /// Id of device that generated the event.
298        pub device_id: DeviceId,
299
300        /// Identify the touch contact or *finger*.
301        ///
302        /// Multiple points of contact can happen in the same device at the same time,
303        /// this ID identifies each uninterrupted contact. IDs are unique only among other concurrent touches
304        /// on the same device, after a touch is ended an ID may be reused.
305        pub touch: TouchId,
306
307        /// Propagation handle for the [`touch`] lifetime.
308        ///
309        /// The [`TOUCH_INPUT_EVENT`] and [`TOUCH_MOVE_EVENT`] have their own separate propagation handles, but
310        /// touch gesture events aggregate all these events to produce a single *gesture event*, usually only a single
311        /// gesture should be generated, multiple gestures can disambiguate using this `touch_propagation` handle.
312        ///
313        /// As an example, [`TOUCH_TAP_EVENT`] only tries to match the gesture if it has subscribers, and only notifies
314        /// if by the time the gesture completes the `touch_propagation` was not stopped. Touch gesture events or event properties
315        /// must stop touch propagation as soon as they commit to a gesture, a *pan* gesture for example, must stop as soon as
316        /// it starts scrolling, otherwise the user may accidentally scroll and tap a button at the same time.
317        ///
318        /// The propagation handle always signals *stopped* after the touch ends. Handles are unique while at least one
319        /// clone of it remains, this makes this a better unique identifier of a touch contact than [`TouchId`] that may be reused
320        /// by the system as soon as a new touch contact is made.
321        ///
322        /// [`touch`]: Self::touch
323        pub touch_propagation: EventPropagationHandle,
324
325        /// Center of the touch in the window's content area.
326        pub position: DipPoint,
327
328        /// Touch pressure force and angle.
329        pub force: Option<TouchForce>,
330
331        /// Velocity in device independent pixels per second.
332        ///
333        /// This is always zero on `Start` and `Cancel` and is the last move velocity for `End`. Note that
334        /// the velocity value can be less than [`min_fling_velocity`].
335        ///
336        /// [`min_fling_velocity`]: TouchConfig::min_fling_velocity
337        pub velocity: DipVector,
338
339        /// Touch phase.
340        ///
341        /// Does not include `Moved`.
342        pub phase: TouchPhase,
343
344        /// Hit-test result for the touch point in the window.
345        pub hits: HitTestInfo,
346
347        /// Full path to the top-most hit in [`hits`](TouchInputArgs::hits).
348        pub target: InteractionPath,
349
350        /// Current pointer capture.
351        pub capture: Option<CaptureInfo>,
352
353        /// What modifier keys where pressed when this event happened.
354        pub modifiers: ModifiersState,
355
356        ..
357
358        /// The [`target`] and [`capture`].
359        ///
360        /// [`target`]: Self::target
361        /// [`capture`]: Self::capture
362        fn delivery_list(&self, list: &mut UpdateDeliveryList) {
363            list.insert_wgt(&self.target);
364            if let Some(c) = &self.capture {
365                list.insert_wgt(&c.target);
366            }
367        }
368    }
369
370    /// Arguments for [`TOUCHED_EVENT`].
371    pub struct TouchedArgs {
372        /// Id of window that received the event.
373        pub window_id: WindowId,
374
375        /// Id of device that generated the event.
376        pub device_id: Option<DeviceId>,
377
378        /// Identify the touch contact or *finger*.
379        ///
380        /// Multiple points of contact can happen in the same device at the same time,
381        /// this ID identifies each uninterrupted contact. IDs are unique only among other concurrent touches
382        /// on the same device, after a touch is ended an ID may be reused.
383        pub touch: TouchId,
384
385        /// Handle across the lifetime of `touch`.
386        ///
387        /// See [`TouchInputArgs::touch_propagation`] for more details.
388        pub touch_propagation: EventPropagationHandle,
389
390        /// Center of the touch in the window's content area.
391        pub position: DipPoint,
392
393        /// Touch pressure force and angle.
394        pub force: Option<TouchForce>,
395
396        /// Touch phase that caused the contact gain or loss with the widget.
397        pub phase: TouchPhase,
398
399        /// Hit-test result for the touch point in the window.
400        pub hits: HitTestInfo,
401
402        /// Previous top-most hit before the touch moved.
403        pub prev_target: Option<InteractionPath>,
404
405        /// Full path to the top-most hit in [`hits`](TouchInputArgs::hits).
406        pub target: Option<InteractionPath>,
407
408        /// Previous pointer capture.
409        pub prev_capture: Option<CaptureInfo>,
410
411        /// Current pointer capture.
412        pub capture: Option<CaptureInfo>,
413
414        ..
415
416        /// The [`prev_target`], [`target`] and [`capture`].
417        ///
418        /// [`prev_target`]: Self::prev_target
419        /// [`target`]: Self::target
420        /// [`capture`]: Self::capture
421        fn delivery_list(&self, list: &mut UpdateDeliveryList) {
422            if let Some(p) = &self.prev_target {
423                list.insert_wgt(p);
424            }
425            if let Some(p) = &self.target {
426                list.insert_wgt(p);
427            }
428            if let Some(c) = &self.capture {
429                list.insert_wgt(&c.target);
430            }
431        }
432    }
433
434    /// Arguments for [`TOUCH_TAP_EVENT`].
435    pub struct TouchTapArgs {
436        /// Id of window that received the event.
437        pub window_id: WindowId,
438
439        /// Id of device that generated the event.
440        pub device_id: DeviceId,
441
442        /// Identify the touch contact or *finger*.
443        ///
444        /// Multiple points of contact can happen in the same device at the same time,
445        /// this ID identifies each uninterrupted contact. IDs are unique only among other concurrent touches
446        /// on the same device, after a touch is ended an ID may be reused.
447        pub touch: TouchId,
448
449        /// Center of the touch in the window's content area.
450        pub position: DipPoint,
451
452        /// Hit-test result for the touch point in the window.
453        pub hits: HitTestInfo,
454
455        /// Full path to the top-most hit in [`hits`](TouchInputArgs::hits).
456        pub target: InteractionPath,
457
458        /// What modifier keys where pressed when this event happened.
459        pub modifiers: ModifiersState,
460
461        /// Count of taps within the double-tap interval. Number `1` is single tap, `2` is double tap, etc.
462        pub tap_count: NonZeroU32,
463
464        ..
465
466        /// The [`target`].
467        ///
468        /// [`target`]: Self::target
469        fn delivery_list(&self, list: &mut UpdateDeliveryList) {
470            list.insert_wgt(&self.target);
471        }
472    }
473
474    /// Arguments for [`TOUCH_LONG_PRESS_EVENT`].
475    pub struct TouchLongPressArgs {
476        /// Id of window that received the event.
477        pub window_id: WindowId,
478
479        /// Id of device that generated the event.
480        pub device_id: DeviceId,
481
482        /// Identify the touch contact or *finger*.
483        ///
484        /// Multiple points of contact can happen in the same device at the same time,
485        /// this ID identifies each uninterrupted contact. IDs are unique only among other concurrent touches
486        /// on the same device, after a touch is ended an ID may be reused.
487        pub touch: TouchId,
488
489        /// Center of the touch in the window's content area.
490        pub position: DipPoint,
491
492        /// Hit-test result for the touch point in the window.
493        pub hits: HitTestInfo,
494
495        /// Full path to the top-most hit in [`hits`](TouchInputArgs::hits).
496        pub target: InteractionPath,
497
498        /// What modifier keys where pressed when this touch started.
499        pub modifiers: ModifiersState,
500
501        /// Timestamp of when the touch started.
502        pub start_time: DInstant,
503
504        ..
505
506        /// The [`target`].
507        ///
508        /// [`target`]: Self::target
509        fn delivery_list(&self, list: &mut UpdateDeliveryList) {
510            list.insert_wgt(&self.target);
511        }
512    }
513
514    /// Arguments for [`TOUCH_TRANSFORM_EVENT`].
515    pub struct TouchTransformArgs {
516        /// Id of window that received the touch events.
517        pub window_id: WindowId,
518
519        /// Id of the device that generated the touch events.
520        pub device_id: DeviceId,
521
522        /// Info collected when the second touch point started.
523        pub first_info: TouchTransformInfo,
524
525        /// Latest update of the two points.
526        pub latest_info: TouchTransformInfo,
527
528        /// Velocity of the `latest_info` touch points.
529        pub velocity: [PxVector; 2],
530
531        /// Scale factor used in the computed pixel values.
532        ///
533        /// This is the window's scale factor when the first touch started.
534        pub scale_factor: Factor,
535
536        /// Gesture phase.
537        pub phase: TouchPhase,
538
539        /// Hit-test result for the center point between the first position of the two touches in the window
540        /// when the gesture started.
541        pub hits: HitTestInfo,
542
543        /// Full path to the top-most hit in [`hits`](TouchInputArgs::hits).
544        pub target: InteractionPath,
545
546        /// Current pointer capture.
547        pub capture: Option<CaptureInfo>,
548
549        /// What modifier keys where pressed when this event happened.
550        pub modifiers: ModifiersState,
551
552        ..
553
554        /// The [`target`] and [`capture`].
555        ///
556        /// [`target`]: Self::target
557        /// [`capture`]: Self::capture
558        fn delivery_list(&self, list: &mut UpdateDeliveryList) {
559            list.insert_wgt(&self.target);
560            if let Some(c) = &self.capture {
561                list.insert_wgt(&c.target);
562            }
563        }
564    }
565}
566
567impl TouchMoveArgs {
568    /// If [`capture`] is `None` or [`allows`] the [`WIDGET`] to receive this event.
569    ///
570    /// [`capture`]: Self::capture
571    /// [`allows`]: CaptureInfo::allows
572    /// [`WIDGET`]: zng_app::widget::WIDGET
573    pub fn capture_allows(&self) -> bool {
574        self.capture.as_ref().map(|c| c.allows()).unwrap_or(true)
575    }
576}
577
578impl TouchInputArgs {
579    /// If [`capture`] is `None` or [`allows`] the [`WIDGET`] to receive this event.
580    ///
581    /// [`capture`]: Self::capture
582    /// [`allows`]: CaptureInfo::allows
583    /// [`WIDGET`]: zng_app::widget::WIDGET
584    pub fn capture_allows(&self) -> bool {
585        self.capture.as_ref().map(|c| c.allows()).unwrap_or(true)
586    }
587
588    /// If the `widget_id` is in the [`target`] is enabled.
589    ///
590    /// [`target`]: Self::target
591    pub fn is_enabled(&self, widget_id: WidgetId) -> bool {
592        self.target.interactivity_of(widget_id).map(|i| i.is_enabled()).unwrap_or(false)
593    }
594
595    /// If the `widget_id` is in the [`target`] is disabled.
596    ///
597    /// [`target`]: Self::target
598    pub fn is_disabled(&self, widget_id: WidgetId) -> bool {
599        self.target.interactivity_of(widget_id).map(|i| i.is_disabled()).unwrap_or(false)
600    }
601
602    /// If the [`phase`] is start.
603    ///
604    /// [`phase`]: Self::phase
605    pub fn is_touch_start(&self) -> bool {
606        matches!(self.phase, TouchPhase::Start)
607    }
608
609    /// If the [`phase`] is end.
610    ///
611    /// [`phase`]: Self::phase
612    pub fn is_touch_end(&self) -> bool {
613        matches!(self.phase, TouchPhase::End)
614    }
615
616    /// If the [`phase`] is cancel.
617    ///
618    /// [`phase`]: Self::phase
619    pub fn is_touch_cancel(&self) -> bool {
620        matches!(self.phase, TouchPhase::Cancel)
621    }
622
623    /// Compute the final offset and duration for a *fling* animation that simulates inertia movement from the
624    /// [`velocity.x`] and `friction`. Returns 0 if velocity less than [`min_fling_velocity`].
625    ///
626    /// Friction is in dips decelerated per second.
627    ///
628    /// To animate a point using these values:
629    ///
630    /// * Compute the final point by adding the vector offset to the current point.
631    /// * Animate using the duration linear interpolation.
632    ///
633    /// [`velocity.x`]: Self::velocity
634    /// [`min_fling_velocity`]: TouchConfig::min_fling_velocity
635    pub fn inertia_x(&self, friction: Dip) -> (Dip, Duration) {
636        Self::inertia(self.velocity.x, friction)
637    }
638
639    /// Compute the final offset and duration for a *fling* animation that simulates inertia movement from the
640    /// [`velocity.y`] and `friction`. Returns 0 if velocity less than [`min_fling_velocity`].
641    ///
642    /// Friction is in dips decelerated per second.
643    ///
644    /// [`velocity.y`]: Self::velocity
645    /// [`min_fling_velocity`]: TouchConfig::min_fling_velocity
646    pub fn inertia_y(&self, friction: Dip) -> (Dip, Duration) {
647        Self::inertia(self.velocity.y, friction)
648    }
649
650    fn inertia(velocity: Dip, friction: Dip) -> (Dip, Duration) {
651        let cfg = TOUCH.touch_config().get();
652        let signal = if velocity >= 0 { 1.0 } else { -1.0 };
653        let velocity = velocity.abs();
654
655        if velocity < cfg.min_fling_velocity {
656            (Dip::new(0), Duration::ZERO)
657        } else {
658            let velocity = velocity.min(cfg.max_fling_velocity).to_f32();
659            let friction = friction.to_f32();
660
661            let time = velocity / friction;
662            let offset = (velocity * time) - (friction * time);
663
664            (Dip::from(offset) * signal, time.secs())
665        }
666    }
667}
668
669impl TouchTapArgs {
670    /// If the `widget_id` is in the [`target`] is enabled.
671    ///
672    /// [`target`]: Self::target
673    pub fn is_enabled(&self, widget_id: WidgetId) -> bool {
674        self.target.interactivity_of(widget_id).map(|i| i.is_enabled()).unwrap_or(false)
675    }
676
677    /// If the `widget_id` is in the [`target`] is disabled.
678    ///
679    /// [`target`]: Self::target
680    pub fn is_disabled(&self, widget_id: WidgetId) -> bool {
681        self.target.interactivity_of(widget_id).map(|i| i.is_disabled()).unwrap_or(false)
682    }
683}
684
685impl TouchLongPressArgs {
686    /// If the `widget_id` is in the [`target`] is enabled.
687    ///
688    /// [`target`]: Self::target
689    pub fn is_enabled(&self, widget_id: WidgetId) -> bool {
690        self.target.interactivity_of(widget_id).map(|i| i.is_enabled()).unwrap_or(false)
691    }
692
693    /// If the `widget_id` is in the [`target`] is disabled.
694    ///
695    /// [`target`]: Self::target
696    pub fn is_disabled(&self, widget_id: WidgetId) -> bool {
697        self.target.interactivity_of(widget_id).map(|i| i.is_disabled()).unwrap_or(false)
698    }
699}
700
701impl TouchedArgs {
702    /// If [`capture`] is `None` or [`allows`] the [`WIDGET`] to receive this event.
703    ///
704    /// [`capture`]: Self::capture
705    /// [`allows`]: CaptureInfo::allows
706    /// [`WIDGET`]: zng_app::widget::WIDGET
707    pub fn capture_allows(&self) -> bool {
708        self.capture.as_ref().map(|c| c.allows()).unwrap_or(true)
709    }
710
711    /// Event caused by the touch position moving over/out of the widget bounds.
712    pub fn is_touch_move(&self) -> bool {
713        self.device_id.is_some()
714    }
715
716    /// Event caused by the widget moving under/out of the mouse position.
717    pub fn is_widget_move(&self) -> bool {
718        self.device_id.is_none()
719    }
720
721    /// Event caused by a pointer capture change.
722    pub fn is_capture_change(&self) -> bool {
723        self.prev_capture != self.capture
724    }
725
726    /// Returns `true` if the [`WIDGET`] was not touched, but now is.
727    ///
728    /// [`WIDGET`]: zng_app::widget::WIDGET
729    pub fn is_touch_enter(&self) -> bool {
730        !self.was_touched() && self.is_touched()
731    }
732
733    /// Returns `true` if the [`WIDGET`] was touched, but now isn't.
734    ///
735    /// [`WIDGET`]: zng_app::widget::WIDGET
736    pub fn is_touch_leave(&self) -> bool {
737        self.was_touched() && !self.is_touched()
738    }
739
740    /// Returns `true` if the [`WIDGET`] was not touched or was disabled, but now is touched and enabled.
741    ///
742    /// [`WIDGET`]: zng_app::widget::WIDGET
743    pub fn is_touch_enter_enabled(&self) -> bool {
744        (!self.was_touched() || self.was_disabled(WIDGET.id())) && self.is_touched() && self.is_enabled(WIDGET.id())
745    }
746
747    /// Returns `true` if the [`WIDGET`] was touched and enabled, but now is not touched or is disabled.
748    ///
749    /// [`WIDGET`]: zng_app::widget::WIDGET
750    pub fn is_touch_leave_enabled(&self) -> bool {
751        self.was_touched() && self.was_enabled(WIDGET.id()) && (!self.is_touched() || self.is_disabled(WIDGET.id()))
752    }
753
754    /// Returns `true` if the [`WIDGET`] was not touched or was enabled, but now is touched and disabled.
755    ///
756    /// [`WIDGET`]: zng_app::widget::WIDGET
757    pub fn is_touch_enter_disabled(&self) -> bool {
758        (!self.was_touched() || self.was_enabled(WIDGET.id())) && self.is_touched() && self.is_disabled(WIDGET.id())
759    }
760
761    /// Returns `true` if the [`WIDGET`] was touched and disabled, but now is not touched or is enabled.
762    ///
763    /// [`WIDGET`]: zng_app::widget::WIDGET
764    pub fn is_touch_leave_disabled(&self) -> bool {
765        self.was_touched() && self.was_disabled(WIDGET.id()) && (!self.is_touched() || self.is_enabled(WIDGET.id()))
766    }
767
768    /// Returns `true` if the [`WIDGET`] is in [`prev_target`] and is allowed by the [`prev_capture`].
769    ///
770    /// [`prev_target`]: Self::prev_target
771    /// [`prev_capture`]: Self::prev_capture
772    /// [`WIDGET`]: zng_app::widget::WIDGET
773    pub fn was_touched(&self) -> bool {
774        if let Some(cap) = &self.prev_capture {
775            if !cap.allows() {
776                return false;
777            }
778        }
779
780        if let Some(t) = &self.prev_target {
781            return t.contains(WIDGET.id());
782        }
783
784        false
785    }
786
787    /// Returns `true` if the [`WIDGET`] is in [`target`] and is allowed by the current [`capture`].
788    ///
789    /// [`target`]: Self::target
790    /// [`capture`]: Self::capture
791    /// [`WIDGET`]: zng_app::widget::WIDGET
792    pub fn is_touched(&self) -> bool {
793        if let Some(cap) = &self.capture {
794            if !cap.allows() {
795                return false;
796            }
797        }
798
799        if let Some(t) = &self.target {
800            return t.contains(WIDGET.id());
801        }
802
803        false
804    }
805
806    /// Returns `true` if the widget was enabled in [`prev_target`].
807    ///
808    /// [`prev_target`]: Self::prev_target
809    pub fn was_enabled(&self, widget_id: WidgetId) -> bool {
810        self.prev_target
811            .as_ref()
812            .and_then(|t| t.interactivity_of(widget_id))
813            .map(|itr| itr.is_enabled())
814            .unwrap_or(false)
815    }
816
817    /// Returns `true` if the widget was disabled in [`prev_target`].
818    ///
819    /// [`prev_target`]: Self::prev_target
820    pub fn was_disabled(&self, widget_id: WidgetId) -> bool {
821        self.prev_target
822            .as_ref()
823            .and_then(|t| t.interactivity_of(widget_id))
824            .map(|itr| itr.is_disabled())
825            .unwrap_or(false)
826    }
827
828    /// Returns `true` if the widget is enabled in [`target`].
829    ///
830    /// [`target`]: Self::target
831    pub fn is_enabled(&self, widget_id: WidgetId) -> bool {
832        self.target
833            .as_ref()
834            .and_then(|t| t.interactivity_of(widget_id))
835            .map(|itr| itr.is_enabled())
836            .unwrap_or(false)
837    }
838
839    /// Returns `true` if the widget is disabled in [`target`].
840    ///
841    /// [`target`]: Self::target
842    pub fn is_disabled(&self, widget_id: WidgetId) -> bool {
843        self.target
844            .as_ref()
845            .and_then(|t| t.interactivity_of(widget_id))
846            .map(|itr| itr.is_disabled())
847            .unwrap_or(false)
848    }
849}
850
851impl TouchTransformArgs {
852    /// If [`capture`] is `None` or [`allows`] the [`WIDGET`] to receive this event.
853    ///
854    /// [`capture`]: Self::capture
855    /// [`allows`]: CaptureInfo::allows
856    /// [`WIDGET`]: zng_app::widget::WIDGET
857    pub fn capture_allows(&self) -> bool {
858        self.capture.as_ref().map(|c| c.allows()).unwrap_or(true)
859    }
860
861    /// If the `widget_id` is in the [`target`] is enabled.
862    ///
863    /// [`target`]: Self::target
864    pub fn is_enabled(&self, widget_id: WidgetId) -> bool {
865        self.target.interactivity_of(widget_id).map(|i| i.is_enabled()).unwrap_or(false)
866    }
867
868    /// If the `widget_id` is in the [`target`] is disabled.
869    ///
870    /// [`target`]: Self::target
871    pub fn is_disabled(&self, widget_id: WidgetId) -> bool {
872        self.target.interactivity_of(widget_id).map(|i| i.is_disabled()).unwrap_or(false)
873    }
874
875    /// Gets the [`first_info`] and [`latest_info`] in the [`WIDGET`] inner bounds space.
876    ///
877    /// [`first_info`]: Self::first_info
878    /// [`latest_info`]: Self::latest_info
879    /// [`WIDGET`]: zng_app::widget::WIDGET
880    pub fn local_info(&self) -> [TouchTransformInfo; 2] {
881        let mut first = self.first_info.clone();
882        let mut latest = self.latest_info.clone();
883
884        let offset = WIDGET.bounds().inner_offset();
885
886        first -= offset;
887        latest -= offset;
888
889        [first, latest]
890    }
891
892    /// Computes the translation to transform from [`first_info`] to [`latest_info`].
893    ///
894    /// [`first_info`]: Self::first_info
895    /// [`latest_info`]: Self::latest_info
896    pub fn translation(&self) -> euclid::Vector2D<f32, Px> {
897        self.first_info.translation(&self.latest_info)
898    }
899
900    /// Computes the translation-x to transform from [`first_info`] to [`latest_info`].
901    ///
902    /// [`first_info`]: Self::first_info
903    /// [`latest_info`]: Self::latest_info
904    pub fn translation_x(&self) -> f32 {
905        self.first_info.translation_x(&self.latest_info)
906    }
907
908    /// Computes the translation-y to transform from [`first_info`] to [`latest_info`].
909    ///
910    /// [`first_info`]: Self::first_info
911    /// [`latest_info`]: Self::latest_info
912    pub fn translation_y(&self) -> f32 {
913        self.first_info.translation_y(&self.latest_info)
914    }
915
916    /// Computes the rotation to transform from [`first_info`] to [`latest_info`].
917    ///
918    /// [`first_info`]: Self::first_info
919    /// [`latest_info`]: Self::latest_info
920    pub fn rotation(&self) -> AngleRadian {
921        self.first_info.rotation(&self.latest_info)
922    }
923
924    /// Computes the scale to transform from [`first_info`] to [`latest_info`].
925    ///
926    /// [`first_info`]: Self::first_info
927    /// [`latest_info`]: Self::latest_info
928    pub fn scale(&self) -> Factor {
929        self.first_info.scale(&self.latest_info)
930    }
931
932    /// Computes the scale-y to transform from [`first_info`] to [`latest_info`].
933    ///
934    /// [`first_info`]: Self::first_info
935    /// [`latest_info`]: Self::latest_info
936    pub fn scale_x(&self) -> Factor {
937        self.first_info.scale_x(&self.latest_info)
938    }
939
940    /// Computes the scale-y to transform from [`first_info`] to [`latest_info`].
941    ///
942    /// [`first_info`]: Self::first_info
943    /// [`latest_info`]: Self::latest_info
944    pub fn scale_y(&self) -> Factor {
945        self.first_info.scale_y(&self.latest_info)
946    }
947
948    /// Computes the transform from [`first_info`] to [`latest_info`].
949    ///
950    /// [`first_info`]: Self::first_info
951    /// [`latest_info`]: Self::latest_info
952    pub fn transform(&self, mode: TouchTransformMode) -> PxTransform {
953        self.first_info.transform(&self.latest_info, mode)
954    }
955
956    /// Computes the transform between the [`local_info`] values, rotates and scales around the latest center.
957    ///
958    /// [`local_info`]: Self::local_info
959    pub fn local_transform(&self, mode: TouchTransformMode) -> PxTransform {
960        let [first, latest] = self.local_info();
961
962        let mut r = first.transform(&latest, mode);
963
964        if mode.contains(TouchTransformMode::ROTATE)
965            || mode.contains(TouchTransformMode::SCALE_X)
966            || mode.contains(TouchTransformMode::SCALE_Y)
967        {
968            let c = latest.center.to_vector();
969            r = PxTransform::Offset(-c).then(&r).then_translate(c);
970        }
971
972        r
973    }
974
975    /// Average velocity.
976    pub fn translation_velocity(&self) -> PxVector {
977        (self.velocity[0] + self.velocity[1]) / Px(2)
978    }
979
980    /// Compute the final offset and duration for a *fling* animation that simulates inertia movement from the
981    /// [`translation_velocity().x`] and `deceleration`. Returns 0 if velocity less than [`min_fling_velocity`].
982    ///
983    /// Deceleration is in dip/s, a good value is 1000. The recommended animation easing function
984    /// is `|t| easing::ease_out(easing::quad, t)`.
985    ///
986    /// [`translation_velocity().x`]: Self::translation_velocity
987    /// [`min_fling_velocity`]: TouchConfig::min_fling_velocity
988    pub fn translation_inertia_x(&self, deceleration: Dip) -> (Px, Duration) {
989        self.inertia((self.velocity[0].x + self.velocity[1].x) / Px(2), deceleration)
990    }
991
992    /// Compute the final offset and duration for a *fling* animation that simulates inertia movement from the
993    /// [`translation_velocity().y`] and `deceleration`. Returns 0 if velocity less than [`min_fling_velocity`].
994    ///
995    /// Deceleration is in dip/s, a good value is 1000. The recommended animation easing function is
996    /// `|t| easing::ease_out(easing::quad, t)`.
997    ///
998    /// [`translation_velocity().y`]: Self::translation_velocity
999    /// [`min_fling_velocity`]: TouchConfig::min_fling_velocity
1000    pub fn translation_inertia_y(&self, deceleration: Dip) -> (Px, Duration) {
1001        self.inertia((self.velocity[0].y + self.velocity[1].y) / Px(2), deceleration)
1002    }
1003
1004    /// If the [`phase`] is start.
1005    ///
1006    /// Note that the [`latest_info`] may already be different from [`first_info`] if the gesture
1007    /// detector awaited to disambiguate before starting the gesture.
1008    ///
1009    /// [`phase`]: Self::phase
1010    /// [`first_info`]: Self::first_info
1011    /// [`latest_info`]: Self::latest_info
1012    pub fn is_start(&self) -> bool {
1013        matches!(self.phase, TouchPhase::Start)
1014    }
1015
1016    /// If the [`phase`] is end.
1017    ///
1018    /// Any transform already applied must be committed.
1019    ///
1020    /// [`phase`]: Self::phase
1021    pub fn is_end(&self) -> bool {
1022        matches!(self.phase, TouchPhase::End)
1023    }
1024
1025    /// If the [`phase`] is cancel.
1026    ///
1027    /// Any transform already applied must be undone.
1028    ///
1029    /// [`phase`]: Self::phase
1030    pub fn is_cancel(&self) -> bool {
1031        matches!(self.phase, TouchPhase::Cancel)
1032    }
1033
1034    fn inertia(&self, velocity: Px, deceleration: Dip) -> (Px, Duration) {
1035        let friction = deceleration.to_px(self.scale_factor);
1036        let cfg = TOUCH.touch_config().get();
1037        let min_fling_velocity = cfg.min_fling_velocity.to_px(self.scale_factor);
1038
1039        let signal = if velocity >= 0 { 1.0 } else { -1.0 };
1040        let velocity = velocity.abs();
1041
1042        if velocity < min_fling_velocity {
1043            (Px(0), Duration::ZERO)
1044        } else {
1045            let max_fling_velocity = cfg.max_fling_velocity.to_px(self.scale_factor);
1046            let velocity = velocity.min(max_fling_velocity).0 as f32;
1047            let friction = friction.0 as f32;
1048
1049            let time = velocity / friction;
1050            let offset = 0.5 * friction * time * time;
1051            (Px(offset.round() as _) * signal, time.secs())
1052        }
1053    }
1054}
1055
1056event! {
1057    /// Touch contact moved.
1058    pub static TOUCH_MOVE_EVENT: TouchMoveArgs;
1059
1060    /// Touch contact started or ended.
1061    pub static TOUCH_INPUT_EVENT: TouchInputArgs;
1062
1063    /// Touch made first contact or lost contact with a widget.
1064    pub static TOUCHED_EVENT: TouchedArgs;
1065
1066    /// Touch tap.
1067    ///
1068    /// This is a touch gesture event, it only notifies if it has listeners, either widget subscribers in the
1069    /// touched path or app level hooks.
1070    pub static TOUCH_TAP_EVENT: TouchTapArgs;
1071
1072    /// Two point touch transform.
1073    ///
1074    /// This is a touch gesture event, it only notifies if it has listeners, either widget subscribers in the
1075    /// touched path or app level hooks.
1076    pub static TOUCH_TRANSFORM_EVENT: TouchTransformArgs;
1077
1078    /// Touch contact pressed without moving for more then the [`tap_max_time`].
1079    ///
1080    /// This is a touch gesture event, it only notifies if it has listeners, either widget subscribers in the
1081    /// touched path or app level hooks.
1082    ///
1083    /// [`tap_max_time`]: TouchConfig::tap_max_time
1084    pub static TOUCH_LONG_PRESS_EVENT: TouchLongPressArgs;
1085}
1086
1087impl AppExtension for TouchManager {
1088    fn event_preview(&mut self, update: &mut EventUpdate) {
1089        if let Some(args) = RAW_FRAME_RENDERED_EVENT.on(update) {
1090            self.continue_pressed(args.window_id);
1091        } else if let Some(args) = RAW_TOUCH_EVENT.on(update) {
1092            let mut pending_move: Vec<TouchMove> = vec![];
1093
1094            for u in &args.touches {
1095                if let TouchPhase::Move = u.phase {
1096                    if let Some(e) = pending_move.iter_mut().find(|e| e.touch == u.touch) {
1097                        e.moves.push((u.position, u.force));
1098                    } else {
1099                        pending_move.push(TouchMove {
1100                            touch: u.touch,
1101                            touch_propagation: if let Some(i) = self.pressed.get(&u.touch) {
1102                                i.touch_propagation.clone()
1103                            } else {
1104                                let weird = EventPropagationHandle::new();
1105                                weird.stop();
1106                                weird
1107                            },
1108                            moves: vec![(u.position, u.force)],
1109                            velocity: DipVector::zero(),
1110                            hits: HitTestInfo::no_hits(args.window_id), // hit-test deferred
1111                            target: InteractionPath::new(args.window_id, []),
1112                        });
1113                    }
1114                } else {
1115                    self.on_move(args, mem::take(&mut pending_move));
1116                    self.on_input(args, u);
1117                }
1118            }
1119
1120            self.on_move(args, pending_move);
1121        } else if let Some(args) = WIDGET_INFO_CHANGED_EVENT.on(update) {
1122            self.continue_pressed(args.window_id);
1123        } else if let Some(args) = MODIFIERS_CHANGED_EVENT.on(update) {
1124            self.modifiers = args.modifiers;
1125        } else if let Some(args) = RAW_TOUCH_CONFIG_CHANGED_EVENT.on(update) {
1126            TOUCH_SV.read().touch_config.set(args.config);
1127        } else if let Some(args) = VIEW_PROCESS_INITED_EVENT.on(update) {
1128            TOUCH_SV.read().touch_config.set(args.touch_config);
1129
1130            if args.is_respawn {
1131                self.tap_gesture.clear();
1132                self.transform_gesture.clear();
1133                self.long_press_gesture.clear();
1134                TOUCH_SV.read().positions.set(vec![]);
1135
1136                for (touch, info) in self.pressed.drain() {
1137                    let args = TouchInputArgs::now(
1138                        info.target.window_id(),
1139                        info.device_id,
1140                        touch,
1141                        info.touch_propagation.clone(),
1142                        DipPoint::splat(Dip::new(-1)),
1143                        None,
1144                        DipVector::zero(),
1145                        TouchPhase::Cancel,
1146                        HitTestInfo::no_hits(info.target.window_id()),
1147                        info.target.clone(),
1148                        None,
1149                        ModifiersState::empty(),
1150                    );
1151                    TOUCH_INPUT_EVENT.notify(args);
1152
1153                    let args = TouchedArgs::now(
1154                        info.target.window_id(),
1155                        info.device_id,
1156                        touch,
1157                        info.touch_propagation,
1158                        DipPoint::splat(Dip::new(-1)),
1159                        None,
1160                        TouchPhase::Cancel,
1161                        HitTestInfo::no_hits(info.target.window_id()),
1162                        info.target,
1163                        None,
1164                        None,
1165                        None,
1166                    );
1167                    TOUCHED_EVENT.notify(args);
1168                }
1169            }
1170        } else if TOUCH_SV.read().touch_from_mouse_events.get() {
1171            use super::mouse::*;
1172
1173            if let Some(args) = MOUSE_MOVE_EVENT.on(update) {
1174                if let Some(id) = self.mouse_touch {
1175                    args.propagation().stop();
1176
1177                    RAW_TOUCH_EVENT.notify(RawTouchArgs::now(
1178                        args.window_id,
1179                        args.device_id,
1180                        vec![TouchUpdate {
1181                            touch: id,
1182                            phase: TouchPhase::Move,
1183                            position: args.position,
1184                            force: None,
1185                        }],
1186                    ));
1187                }
1188            } else if let Some(args) = MOUSE_INPUT_EVENT.on(update) {
1189                if args.button == super::mouse::MouseButton::Left {
1190                    args.propagation().stop();
1191
1192                    let phase = match args.state {
1193                        ButtonState::Pressed => {
1194                            if self.mouse_touch.is_some() {
1195                                return;
1196                            }
1197                            self.mouse_touch = Some(TouchId(u64::MAX));
1198                            TouchPhase::Start
1199                        }
1200                        ButtonState::Released => {
1201                            if self.mouse_touch.is_none() {
1202                                return;
1203                            }
1204                            self.mouse_touch = None;
1205                            TouchPhase::End
1206                        }
1207                    };
1208
1209                    RAW_TOUCH_EVENT.notify(RawTouchArgs::now(
1210                        args.window_id,
1211                        args.device_id.unwrap_or(DeviceId::new_unique()),
1212                        vec![TouchUpdate {
1213                            touch: TouchId(u64::MAX),
1214                            phase,
1215                            position: args.position,
1216                            force: None,
1217                        }],
1218                    ));
1219                }
1220            } else if let Some(args) = RAW_MOUSE_LEFT_EVENT.on(update) {
1221                if let Some(id) = self.mouse_touch.take() {
1222                    RAW_TOUCH_EVENT.notify(RawTouchArgs::now(
1223                        args.window_id,
1224                        args.device_id,
1225                        vec![TouchUpdate {
1226                            touch: id,
1227                            phase: TouchPhase::Cancel,
1228                            position: DipPoint::zero(),
1229                            force: None,
1230                        }],
1231                    ))
1232                }
1233            }
1234        }
1235    }
1236
1237    fn event(&mut self, update: &mut EventUpdate) {
1238        if let Some(args) = TOUCH_INPUT_EVENT.on(update) {
1239            self.tap_gesture.on_input(args);
1240            self.transform_gesture.on_input(args);
1241            self.long_press_gesture.on_input(args);
1242        } else if let Some(args) = TOUCH_MOVE_EVENT.on(update) {
1243            self.tap_gesture.on_move(args);
1244            self.transform_gesture.on_move(args);
1245            self.long_press_gesture.on_move(args);
1246        } else if let Some(args) = POINTER_CAPTURE_EVENT.on(update) {
1247            for (touch, info) in &self.pressed {
1248                let args = TouchedArgs::now(
1249                    info.target.window_id(),
1250                    info.device_id,
1251                    *touch,
1252                    info.touch_propagation.clone(),
1253                    info.position,
1254                    info.force,
1255                    TouchPhase::Move,
1256                    info.hits.clone(),
1257                    info.target.clone(),
1258                    info.target.clone(),
1259                    args.prev_capture.clone(),
1260                    args.new_capture.clone(),
1261                );
1262                TOUCHED_EVENT.notify(args);
1263            }
1264        }
1265    }
1266
1267    fn update_preview(&mut self) {
1268        self.long_press_gesture.on_update();
1269    }
1270}
1271impl TouchManager {
1272    fn on_input(&mut self, args: &RawTouchArgs, update: &TouchUpdate) {
1273        if let Ok(w) = WINDOWS.widget_tree(args.window_id) {
1274            let mut hits = w.root().hit_test(update.position.to_px(w.scale_factor()));
1275            let mut target = hits
1276                .target()
1277                .and_then(|t| w.get(t.widget_id))
1278                .map(|t| t.interaction_path())
1279                .unwrap_or_else(|| w.root().interaction_path());
1280            let mut position = update.position;
1281
1282            // hit-test for nested windows
1283            if let Some(wgt) = w.get(target.widget_id()) {
1284                if let Some(w) = wgt.nested_window_tree() {
1285                    let f = w.scale_factor();
1286                    let p = update.position.to_px(f);
1287                    let p = wgt.inner_transform().inverse().and_then(|t| t.transform_point(p)).unwrap_or(p);
1288                    position = p.to_dip(f);
1289                    hits = w.root().hit_test(p);
1290                    target = hits
1291                        .target()
1292                        .and_then(|t| w.get(t.widget_id))
1293                        .map(|t| t.interaction_path())
1294                        .unwrap_or_else(|| w.root().interaction_path());
1295                }
1296            }
1297
1298            let target = match target.unblocked() {
1299                Some(t) => t,
1300                None => return, // entire window blocked
1301            };
1302
1303            let capture_info = POINTER_CAPTURE.current_capture_value();
1304
1305            let (gesture_handle, velocity) = match update.phase {
1306                TouchPhase::Start => {
1307                    let handle = EventPropagationHandle::new();
1308                    if let Some(weird) = self.pressed.insert(
1309                        update.touch,
1310                        PressedInfo {
1311                            touch_propagation: handle.clone(),
1312                            target: target.clone(),
1313                            device_id: args.device_id,
1314                            position,
1315                            force: update.force,
1316                            hits: hits.clone(),
1317                            velocity_samples: vec![], // skip input (will only have velocity after 4 moves)
1318                        },
1319                    ) {
1320                        weird.touch_propagation.stop();
1321                    }
1322                    (handle, DipVector::zero())
1323                }
1324                TouchPhase::End => {
1325                    if let Some(handle) = self.pressed.remove(&update.touch) {
1326                        let vel = handle.velocity();
1327                        (handle.touch_propagation, vel)
1328                    } else {
1329                        let weird = EventPropagationHandle::new();
1330                        weird.stop();
1331                        (weird, DipVector::zero())
1332                    }
1333                }
1334                TouchPhase::Cancel => {
1335                    let handle = self.pressed.remove(&update.touch).map(|i| i.touch_propagation).unwrap_or_default();
1336                    handle.stop();
1337                    (handle, DipVector::zero())
1338                }
1339                TouchPhase::Move => unreachable!(),
1340            };
1341
1342            match update.phase {
1343                TouchPhase::Start => {
1344                    let pos_info = TouchPosition {
1345                        window_id: hits.window_id(),
1346                        touch: update.touch,
1347                        position,
1348                        start_time: args.timestamp,
1349                        update_time: args.timestamp,
1350                    };
1351                    TOUCH_SV.read().positions.modify(move |p| {
1352                        let p = p.to_mut();
1353                        if let Some(weird) = p.iter().position(|p| p.touch == pos_info.touch) {
1354                            p.remove(weird);
1355                        }
1356                        p.push(pos_info);
1357                    });
1358                }
1359                _ => {
1360                    let touch = update.touch;
1361                    TOUCH_SV.read().positions.modify(move |p| {
1362                        if let Some(i) = p.iter().position(|p| p.touch == touch) {
1363                            p.to_mut().remove(i);
1364                        }
1365                    });
1366                }
1367            }
1368
1369            let args = TouchInputArgs::now(
1370                hits.window_id(),
1371                args.device_id,
1372                update.touch,
1373                gesture_handle,
1374                position,
1375                update.force,
1376                velocity,
1377                update.phase,
1378                hits,
1379                target,
1380                capture_info,
1381                self.modifiers,
1382            );
1383
1384            let touched_args = {
1385                // touched
1386
1387                let (prev_target, target) = match args.phase {
1388                    TouchPhase::Start => (None, Some(args.target.clone())),
1389                    TouchPhase::End | TouchPhase::Cancel => (Some(args.target.clone()), None),
1390                    TouchPhase::Move => unreachable!(),
1391                };
1392
1393                TouchedArgs::now(
1394                    args.window_id,
1395                    args.device_id,
1396                    args.touch,
1397                    args.touch_propagation.clone(),
1398                    args.position,
1399                    args.force,
1400                    args.phase,
1401                    args.hits.clone(),
1402                    prev_target,
1403                    target,
1404                    args.capture.clone(),
1405                    args.capture.clone(),
1406                )
1407            };
1408
1409            TOUCH_INPUT_EVENT.notify(args);
1410            TOUCHED_EVENT.notify(touched_args);
1411        } else {
1412            // did not find window, cleanup touched
1413            for u in &args.touches {
1414                if let Some(i) = self.pressed.remove(&u.touch) {
1415                    let capture = POINTER_CAPTURE.current_capture_value();
1416                    let args = TouchedArgs::now(
1417                        args.window_id,
1418                        args.device_id,
1419                        u.touch,
1420                        i.touch_propagation,
1421                        u.position,
1422                        u.force,
1423                        u.phase,
1424                        HitTestInfo::no_hits(args.window_id),
1425                        Some(i.target),
1426                        None,
1427                        capture.clone(),
1428                        capture,
1429                    );
1430                    TOUCHED_EVENT.notify(args);
1431                }
1432            }
1433        }
1434    }
1435
1436    fn on_move(&mut self, args: &RawTouchArgs, mut moves: Vec<TouchMove>) {
1437        if !moves.is_empty() {
1438            if let Ok(w) = WINDOWS.widget_tree(args.window_id) {
1439                let mut window_blocked_remove = vec![];
1440                for m in &mut moves {
1441                    m.hits = w.root().hit_test(m.position().to_px(w.scale_factor()));
1442                    let target = m
1443                        .hits
1444                        .target()
1445                        .and_then(|t| w.get(t.widget_id))
1446                        .map(|t| t.interaction_path())
1447                        .unwrap_or_else(|| w.root().interaction_path());
1448
1449                    match target.unblocked() {
1450                        Some(t) => {
1451                            m.target = t;
1452                            // hit-test for nested windows
1453                            if let Some(wgt) = w.get(m.target.widget_id()) {
1454                                if let Some(w) = wgt.nested_window_tree() {
1455                                    let transform = wgt.inner_transform().inverse();
1456                                    let factor = w.scale_factor();
1457                                    let mut position = PxPoint::zero(); // last
1458                                    for (mv, _) in &mut m.moves {
1459                                        let p = mv.to_px(factor);
1460                                        let p = transform.and_then(|t| t.transform_point(p)).unwrap_or(p);
1461                                        *mv = p.to_dip(factor);
1462                                        position = p;
1463                                    }
1464                                    m.hits = w.root().hit_test(position);
1465                                    let target = m
1466                                        .hits
1467                                        .target()
1468                                        .and_then(|t| w.get(t.widget_id))
1469                                        .map(|t| t.interaction_path())
1470                                        .unwrap_or_else(|| w.root().interaction_path());
1471
1472                                    match target.unblocked() {
1473                                        Some(t) => m.target = t,
1474                                        None => window_blocked_remove.push(m.touch),
1475                                    }
1476                                }
1477                            }
1478                        }
1479                        None => {
1480                            window_blocked_remove.push(m.touch);
1481                        }
1482                    }
1483                }
1484
1485                let position_updates: Vec<_> = moves
1486                    .iter()
1487                    .map(|m| TouchPosition {
1488                        window_id: args.window_id,
1489                        touch: m.touch,
1490                        position: m.position(),
1491                        start_time: args.timestamp, // ignored
1492                        update_time: args.timestamp,
1493                    })
1494                    .collect();
1495                TOUCH_SV.read().positions.modify(move |p| {
1496                    for mut update in position_updates {
1497                        if let Some(i) = p.iter().position(|p| p.touch == update.touch) {
1498                            update.start_time = p[i].start_time;
1499                            p.to_mut()[i] = update;
1500                        }
1501                    }
1502                });
1503
1504                let capture_info = POINTER_CAPTURE.current_capture_value();
1505
1506                let mut touched_events = vec![];
1507
1508                for touch in window_blocked_remove {
1509                    let touch_move = moves.iter().position(|t| t.touch == touch).unwrap();
1510                    moves.swap_remove(touch_move);
1511
1512                    if let Some(i) = self.pressed.remove(&touch) {
1513                        i.touch_propagation.stop();
1514                        let args = TouchedArgs::now(
1515                            args.window_id,
1516                            args.device_id,
1517                            touch,
1518                            i.touch_propagation,
1519                            DipPoint::splat(Dip::new(-1)),
1520                            None,
1521                            TouchPhase::Cancel,
1522                            HitTestInfo::no_hits(args.window_id),
1523                            i.target,
1524                            None,
1525                            None,
1526                            None,
1527                        );
1528                        touched_events.push(args);
1529                    }
1530                }
1531                for m in &mut moves {
1532                    if let Some(i) = self.pressed.get_mut(&m.touch) {
1533                        let (position, force) = *m.moves.last().unwrap();
1534                        i.push_velocity_sample(args.timestamp, position);
1535                        m.velocity = i.velocity();
1536                        i.position = position;
1537                        i.force = force;
1538                        i.hits = m.hits.clone();
1539                        if i.target != m.target {
1540                            let args = TouchedArgs::now(
1541                                args.window_id,
1542                                args.device_id,
1543                                m.touch,
1544                                m.touch_propagation.clone(),
1545                                position,
1546                                force,
1547                                TouchPhase::Move,
1548                                m.hits.clone(),
1549                                i.target.clone(),
1550                                m.target.clone(),
1551                                capture_info.clone(),
1552                                capture_info.clone(),
1553                            );
1554                            i.target = m.target.clone();
1555                            touched_events.push(args);
1556                        }
1557                    }
1558                }
1559
1560                if !moves.is_empty() {
1561                    let args = TouchMoveArgs::now(args.window_id, args.device_id, moves, capture_info, self.modifiers);
1562                    TOUCH_MOVE_EVENT.notify(args);
1563                }
1564
1565                for args in touched_events {
1566                    TOUCHED_EVENT.notify(args);
1567                }
1568            }
1569        }
1570    }
1571
1572    fn continue_pressed(&mut self, window_id: WindowId) {
1573        let mut tree = None;
1574
1575        let mut window_blocked_remove = vec![];
1576
1577        for (touch, info) in &mut self.pressed {
1578            if info.target.window_id() != window_id {
1579                continue;
1580            }
1581
1582            let tree = tree.get_or_insert_with(|| WINDOWS.widget_tree(window_id).unwrap());
1583            info.hits = tree.root().hit_test(info.position.to_px(tree.scale_factor()));
1584
1585            let target = if let Some(t) = info.hits.target() {
1586                tree.get(t.widget_id).map(|w| w.interaction_path()).unwrap_or_else(|| {
1587                    tracing::error!("hits target `{}` not found", t.widget_id);
1588                    tree.root().interaction_path()
1589                })
1590            } else {
1591                tree.root().interaction_path()
1592            }
1593            .unblocked();
1594
1595            if let Some(target) = target {
1596                if info.target != target {
1597                    let capture = POINTER_CAPTURE.current_capture_value();
1598                    let prev = mem::replace(&mut info.target, target.clone());
1599
1600                    let args = TouchedArgs::now(
1601                        info.target.window_id(),
1602                        None,
1603                        *touch,
1604                        info.touch_propagation.clone(),
1605                        info.position,
1606                        info.force,
1607                        TouchPhase::Move,
1608                        info.hits.clone(),
1609                        prev,
1610                        target,
1611                        capture.clone(),
1612                        capture,
1613                    );
1614                    TOUCHED_EVENT.notify(args);
1615                }
1616            } else {
1617                window_blocked_remove.push(*touch);
1618            }
1619        }
1620
1621        for touch in window_blocked_remove {
1622            if let Some(i) = self.pressed.remove(&touch) {
1623                i.touch_propagation.stop();
1624                let args = TouchedArgs::now(
1625                    i.target.window_id(),
1626                    None,
1627                    touch,
1628                    i.touch_propagation,
1629                    DipPoint::splat(Dip::new(-1)),
1630                    None,
1631                    TouchPhase::Cancel,
1632                    HitTestInfo::no_hits(i.target.window_id()),
1633                    i.target,
1634                    None,
1635                    None,
1636                    None,
1637                );
1638                TOUCHED_EVENT.notify(args);
1639            }
1640        }
1641    }
1642}
1643
1644struct PendingDoubleTap {
1645    window_id: WindowId,
1646    device_id: DeviceId,
1647    target: WidgetId,
1648    count: NonZeroU32,
1649    timestamp: DInstant,
1650}
1651struct PendingTap {
1652    window_id: WindowId,
1653    device_id: DeviceId,
1654    touch: TouchId,
1655    target: WidgetId,
1656
1657    propagation: EventPropagationHandle,
1658}
1659impl PendingTap {
1660    /// Check if the tap is still possible after a touch move..
1661    ///
1662    /// Returns `true` if it is.
1663    fn retain(&self, window_id: WindowId, device_id: DeviceId, touch: TouchId) -> bool {
1664        if self.propagation.is_stopped() {
1665            // cancel, gesture opportunity handled.
1666            return false;
1667        }
1668
1669        if window_id != self.window_id || device_id != self.device_id {
1670            // cancel, not same source or target.
1671            return false;
1672        }
1673
1674        if touch != self.touch {
1675            // cancel, multi-touch.
1676            return false;
1677        }
1678
1679        // retain
1680        true
1681    }
1682}
1683
1684struct PendingLongPress {
1685    window_id: WindowId,
1686    device_id: DeviceId,
1687    touch: TouchId,
1688    target: WidgetId,
1689    position: DipPoint,
1690    start_time: DInstant,
1691    modifiers: ModifiersState,
1692
1693    propagation: EventPropagationHandle,
1694
1695    delay: DeadlineVar,
1696    canceled: bool,
1697}
1698
1699#[derive(Default)]
1700struct LongPressGesture {
1701    pending: Option<PendingLongPress>,
1702}
1703impl LongPressGesture {
1704    fn on_input(&mut self, args: &TouchInputArgs) {
1705        match args.phase {
1706            TouchPhase::Start => {
1707                if let Some(p) = &mut self.pending {
1708                    // only valid if single touch contact, we use the `pending` presence to track this.
1709                    p.canceled = true;
1710                } else if TOUCH_LONG_PRESS_EVENT.has_hooks()
1711                    || args.target.widgets_path().iter().any(|w| TOUCH_LONG_PRESS_EVENT.is_subscriber(*w))
1712                {
1713                    self.pending = Some(PendingLongPress {
1714                        window_id: args.window_id,
1715                        device_id: args.device_id,
1716                        touch: args.touch,
1717                        position: args.position,
1718                        start_time: args.timestamp,
1719                        modifiers: args.modifiers,
1720                        target: args.target.widget_id(),
1721                        propagation: args.touch_propagation.clone(),
1722                        delay: TIMERS.deadline(TOUCH.touch_config().get().tap_max_time),
1723                        canceled: false,
1724                    });
1725                }
1726            }
1727            TouchPhase::End | TouchPhase::Cancel => {
1728                if let Some(p) = &self.pending {
1729                    if args.touch_propagation == p.propagation {
1730                        self.pending = None;
1731                    }
1732                }
1733            }
1734            TouchPhase::Move => unreachable!(),
1735        }
1736    }
1737
1738    fn on_move(&mut self, args: &TouchMoveArgs) {
1739        if let Some(p) = &mut self.pending {
1740            if !p.canceled && !p.propagation.is_stopped() {
1741                for m in &args.touches {
1742                    if p.propagation == m.touch_propagation {
1743                        let dist = p.position - m.position().to_vector();
1744                        let max = TOUCH.touch_config().get().tap_area;
1745                        if dist.x.abs() > max.width || dist.y.abs() > max.height {
1746                            p.canceled = true;
1747                            break;
1748                        }
1749                    } else {
1750                        p.canceled = true;
1751                        break;
1752                    }
1753                }
1754            }
1755        }
1756    }
1757
1758    fn on_update(&mut self) {
1759        if let Some(p) = &mut self.pending {
1760            if !p.canceled && !p.propagation.is_stopped() && p.delay.get().has_elapsed() {
1761                if let Ok(w) = WINDOWS.widget_tree(p.window_id) {
1762                    if let Some(w) = w.get(p.target) {
1763                        let hits = w.hit_test(p.position.to_px(w.tree().scale_factor()));
1764                        if hits.contains(p.target) {
1765                            p.propagation.stop();
1766
1767                            let args = TouchLongPressArgs::now(
1768                                p.window_id,
1769                                p.device_id,
1770                                p.touch,
1771                                p.position,
1772                                hits,
1773                                w.interaction_path(),
1774                                p.modifiers,
1775                                p.start_time,
1776                            );
1777                            TOUCH_LONG_PRESS_EVENT.notify(args);
1778                            return;
1779                        }
1780                    }
1781                }
1782                p.canceled = true;
1783            }
1784        }
1785    }
1786
1787    fn clear(&mut self) {
1788        self.pending = None;
1789    }
1790}
1791
1792#[derive(Default)]
1793struct TapGesture {
1794    pending_double: Option<PendingDoubleTap>,
1795    pending: Option<PendingTap>,
1796}
1797impl TapGesture {
1798    fn on_input(&mut self, args: &TouchInputArgs) {
1799        match args.phase {
1800            TouchPhase::Start => {
1801                if self.pending.is_some() {
1802                    self.pending = None;
1803                    self.pending_double = None;
1804                } else if TOUCH_TAP_EVENT.has_hooks() || args.target.widgets_path().iter().any(|w| TOUCH_TAP_EVENT.is_subscriber(*w)) {
1805                    self.pending = Some(PendingTap {
1806                        window_id: args.window_id,
1807                        device_id: args.device_id,
1808                        touch: args.touch,
1809                        target: args.target.widget_id(),
1810                        propagation: args.touch_propagation.clone(),
1811                    });
1812                }
1813            }
1814            TouchPhase::End => {
1815                let pending_double = self.pending_double.take();
1816
1817                if let Some(p) = self.pending.take() {
1818                    if !p.retain(args.window_id, args.device_id, args.touch) {
1819                        return;
1820                    }
1821
1822                    p.propagation.stop(); // touch_propagation always is stopped after touch end.
1823
1824                    let tree = if let Ok(w) = WINDOWS.widget_tree(args.window_id) {
1825                        w
1826                    } else {
1827                        return;
1828                    };
1829
1830                    match tree.get(p.target) {
1831                        Some(t) => {
1832                            if !t.hit_test(args.position.to_px(tree.scale_factor())).contains(p.target) {
1833                                // cancel, touch did not end over target.
1834                                return;
1835                            }
1836                        }
1837                        None => return,
1838                    }
1839
1840                    if let Some(target) = args.target.sub_path(p.target) {
1841                        let tap_count = if let Some(double) = pending_double {
1842                            let cfg = TOUCH.touch_config().get();
1843                            if double.window_id == p.window_id
1844                                && double.device_id == p.device_id
1845                                && double.target == p.target
1846                                && double.timestamp.elapsed() <= cfg.double_tap_max_time
1847                            {
1848                                NonZeroU32::new(double.count.get() + 1).unwrap()
1849                            } else {
1850                                NonZeroU32::new(1).unwrap()
1851                            }
1852                        } else {
1853                            NonZeroU32::new(1).unwrap()
1854                        };
1855
1856                        self.pending_double = Some(PendingDoubleTap {
1857                            window_id: args.window_id,
1858                            device_id: args.device_id,
1859                            target: p.target,
1860                            count: tap_count,
1861                            timestamp: args.timestamp,
1862                        });
1863
1864                        TOUCH_TAP_EVENT.notify(TouchTapArgs::new(
1865                            args.timestamp,
1866                            args.propagation().clone(),
1867                            p.window_id,
1868                            p.device_id,
1869                            p.touch,
1870                            args.position,
1871                            args.hits.clone(),
1872                            target.into_owned(),
1873                            args.modifiers,
1874                            tap_count,
1875                        ));
1876                    }
1877                }
1878            }
1879            TouchPhase::Cancel => {
1880                if let Some(p) = self.pending.take() {
1881                    p.propagation.stop();
1882                }
1883                self.pending = None;
1884                self.pending_double = None;
1885            }
1886            TouchPhase::Move => unreachable!(),
1887        }
1888    }
1889
1890    fn on_move(&mut self, args: &TouchMoveArgs) {
1891        if let Some(p) = &self.pending {
1892            for t in &args.touches {
1893                if !p.retain(args.window_id, args.device_id, t.touch) {
1894                    self.pending = None;
1895                    self.pending_double = None;
1896                    break;
1897                }
1898            }
1899        }
1900    }
1901
1902    fn clear(&mut self) {
1903        self.pending = None;
1904        self.pending_double = None;
1905    }
1906}
1907
1908/// Info useful for touch gestures computed from two touch points.
1909#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
1910pub struct TouchTransformInfo {
1911    /// The two touch contact points.
1912    pub touches: [euclid::Point2D<f32, Px>; 2],
1913
1914    /// Middle of the line between the two points.
1915    pub center: euclid::Point2D<f32, Px>,
1916
1917    /// Deviation from the points to the center.
1918    ///
1919    /// Min 1.0
1920    pub deviation: f32,
1921
1922    /// Deviation from the points.x to the center.x.
1923    ///
1924    /// Min 1.0
1925    pub deviation_x: f32,
1926
1927    /// Deviation from the points.y to the center.y.
1928    ///
1929    /// Min 1.0
1930    pub deviation_y: f32,
1931
1932    /// Angle of the line.
1933    pub angle: AngleRadian,
1934}
1935impl TouchTransformInfo {
1936    /// Compute the line info.
1937    pub fn new_f32(touches: [euclid::Point2D<f32, Px>; 2]) -> Self {
1938        let a = touches[0].to_vector();
1939        let b = touches[1].to_vector();
1940
1941        let center = (a + b) / 2.0;
1942        let deviation = (a - center).length();
1943        let deviation_x = (a.x - center.x).abs();
1944        let deviation_y = (a.y - center.y).abs();
1945
1946        Self {
1947            touches,
1948            center: center.to_point(),
1949            deviation: deviation.max(1.0),
1950            deviation_x: deviation_x.max(1.0),
1951            deviation_y: deviation_y.max(1.0),
1952            angle: AngleRadian((a.y - b.y).atan2(a.x - b.x)),
1953        }
1954    }
1955
1956    /// Compute the line info, from round pixels.
1957    pub fn new(touches: [PxPoint; 2]) -> Self {
1958        Self::new_f32([touches[0].to_f32(), touches[1].to_f32()])
1959    }
1960
1961    /// Compute the line info, from device independent pixels.
1962    pub fn new_dip(touches: [DipPoint; 2], scale_factor: Factor) -> Self {
1963        Self::new_f32([touches[0].to_f32().to_px(scale_factor), touches[1].to_f32().to_px(scale_factor)])
1964    }
1965}
1966impl TouchTransformInfo {
1967    /// Computes the translation to transform from `self` to `other`.
1968    pub fn translation(&self, other: &Self) -> euclid::Vector2D<f32, Px> {
1969        other.center.to_vector() - self.center.to_vector()
1970    }
1971
1972    /// Computes the translation-x to transform from `self` to `other`.
1973    pub fn translation_x(&self, other: &Self) -> f32 {
1974        other.center.x - self.center.x
1975    }
1976
1977    /// Computes the translation-y to transform from `self` to `other`.
1978    pub fn translation_y(&self, other: &Self) -> f32 {
1979        other.center.y - self.center.y
1980    }
1981
1982    /// Computes the rotation to transform from `self` to `other`.
1983    pub fn rotation(&self, other: &Self) -> AngleRadian {
1984        other.angle - self.angle
1985    }
1986
1987    /// Computes the scale to transform from `self` to `other`.
1988    pub fn scale(&self, other: &Self) -> Factor {
1989        Factor(other.deviation / self.deviation)
1990    }
1991
1992    /// Computes the scale-y to transform from `self` to `other`.
1993    pub fn scale_x(&self, other: &Self) -> Factor {
1994        Factor(other.deviation_x / self.deviation_x)
1995    }
1996
1997    /// Computes the scale-y to transform from `self` to `other`.
1998    pub fn scale_y(&self, other: &Self) -> Factor {
1999        Factor(other.deviation_y / self.deviation_y)
2000    }
2001
2002    /// Computes the transform from `self` to `other`.
2003    pub fn transform(&self, other: &Self, mode: TouchTransformMode) -> PxTransform {
2004        let mut m = PxTransform::identity();
2005
2006        if mode.contains(TouchTransformMode::TRANSLATE) {
2007            m = m.then_translate(self.translation(other));
2008        } else if mode.contains(TouchTransformMode::TRANSLATE_X) {
2009            let t = euclid::vec2(self.translation_x(other), 0.0);
2010            m = m.then_translate(t);
2011        } else if mode.contains(TouchTransformMode::TRANSLATE_Y) {
2012            let t = euclid::vec2(0.0, self.translation_y(other));
2013            m = m.then_translate(t);
2014        }
2015
2016        if mode.contains(TouchTransformMode::SCALE) {
2017            let s = self.scale(other).0;
2018            m = m.then(&PxTransform::scale(s, s));
2019        } else if mode.contains(TouchTransformMode::SCALE_X) {
2020            let s = self.scale_x(other);
2021            m = m.then(&PxTransform::scale(s.0, 1.0))
2022        } else if mode.contains(TouchTransformMode::SCALE_Y) {
2023            let s = self.scale_y(other);
2024            m = m.then(&PxTransform::scale(1.0, s.0))
2025        }
2026
2027        if mode.contains(TouchTransformMode::ROTATE) {
2028            let a = self.rotation(other);
2029            m = m.then(&PxTransform::rotation(0.0, 0.0, a.into()));
2030        }
2031
2032        m
2033    }
2034
2035    /// If the transform is only a translate calculated from a single touch contact.
2036    pub fn is_single(&self) -> bool {
2037        self.touches[0] == self.touches[1]
2038    }
2039}
2040impl ops::AddAssign<euclid::Vector2D<f32, Px>> for TouchTransformInfo {
2041    fn add_assign(&mut self, rhs: euclid::Vector2D<f32, Px>) {
2042        self.touches[0] += rhs;
2043        self.touches[1] += rhs;
2044        self.center += rhs;
2045    }
2046}
2047impl ops::Add<euclid::Vector2D<f32, Px>> for TouchTransformInfo {
2048    type Output = Self;
2049
2050    fn add(mut self, rhs: euclid::Vector2D<f32, Px>) -> Self::Output {
2051        self += rhs;
2052        self
2053    }
2054}
2055impl ops::AddAssign<PxVector> for TouchTransformInfo {
2056    fn add_assign(&mut self, rhs: PxVector) {
2057        *self += rhs.cast::<f32>();
2058    }
2059}
2060impl ops::Add<PxVector> for TouchTransformInfo {
2061    type Output = Self;
2062
2063    fn add(mut self, rhs: PxVector) -> Self::Output {
2064        self += rhs;
2065        self
2066    }
2067}
2068impl ops::SubAssign<euclid::Vector2D<f32, Px>> for TouchTransformInfo {
2069    fn sub_assign(&mut self, rhs: euclid::Vector2D<f32, Px>) {
2070        self.touches[0] -= rhs;
2071        self.touches[1] -= rhs;
2072        self.center -= rhs;
2073    }
2074}
2075impl ops::Sub<euclid::Vector2D<f32, Px>> for TouchTransformInfo {
2076    type Output = Self;
2077
2078    fn sub(mut self, rhs: euclid::Vector2D<f32, Px>) -> Self::Output {
2079        self -= rhs;
2080        self
2081    }
2082}
2083impl ops::SubAssign<PxVector> for TouchTransformInfo {
2084    fn sub_assign(&mut self, rhs: PxVector) {
2085        *self -= rhs.cast::<f32>();
2086    }
2087}
2088impl ops::Sub<PxVector> for TouchTransformInfo {
2089    type Output = Self;
2090
2091    fn sub(mut self, rhs: PxVector) -> Self::Output {
2092        self -= rhs;
2093        self
2094    }
2095}
2096
2097bitflags! {
2098    /// Defines the different transforms that a touch transform can do to keep
2099    /// two touch points in a widget aligned with the touch contacts.
2100    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
2101    #[serde(transparent)]
2102    pub struct TouchTransformMode: u8 {
2103        /// Can translate in the X dimension.
2104        const TRANSLATE_X = 0b0000_0001;
2105        /// Can translate in the y dimension.
2106        const TRANSLATE_Y = 0b0000_0010;
2107        /// Can translate in both dimensions.
2108        const TRANSLATE = Self::TRANSLATE_X.bits() | Self::TRANSLATE_Y.bits();
2109
2110        /// Can scale in the X dimension.
2111        const SCALE_X = 0b0000_0100;
2112        /// Can scale in the Y dimension.
2113        const SCALE_Y = 0b0000_1000;
2114        /// Can scale in both dimensions the same amount.
2115        const SCALE = 0b0001_1100;
2116
2117        /// Can rotate.
2118        const ROTATE = 0b0010_0000;
2119
2120        /// Can translate, scale-square and rotate.
2121        const ALL = Self::TRANSLATE.bits()| Self::SCALE.bits() | Self::ROTATE.bits();
2122    }
2123}
2124impl_from_and_into_var! {
2125    fn from(all_or_empty: bool) -> TouchTransformMode {
2126        if all_or_empty {
2127            TouchTransformMode::ALL
2128        } else {
2129            TouchTransformMode::empty()
2130        }
2131    }
2132}
2133
2134#[derive(Default)]
2135enum TransformGesture {
2136    #[default]
2137    NoStartedZero,
2138
2139    NotStartedOne {
2140        window_id: WindowId,
2141        device_id: DeviceId,
2142        start_position: DipPoint,
2143        position: DipPoint,
2144        handle: EventPropagationHandle,
2145    },
2146    NotStartedTwo {
2147        window_id: WindowId,
2148        device_id: DeviceId,
2149        start_position: [DipPoint; 2],
2150        position: [DipPoint; 2],
2151        handle: [EventPropagationHandle; 2],
2152        scale_factor: Factor,
2153    },
2154
2155    StartedOne {
2156        window_id: WindowId,
2157        device_id: DeviceId,
2158        position: DipPoint,
2159        velocity: DipVector,
2160        scale_factor: Factor,
2161        handle: EventPropagationHandle,
2162        first_info: TouchTransformInfo,
2163        hits: HitTestInfo,
2164        target: InteractionPath,
2165    },
2166    StartedTwo {
2167        window_id: WindowId,
2168        device_id: DeviceId,
2169        position: [DipPoint; 2],
2170        velocity: [DipVector; 2],
2171        scale_factor: Factor,
2172        handle: [EventPropagationHandle; 2],
2173        first_info: TouchTransformInfo,
2174        hits: HitTestInfo,
2175        target: InteractionPath,
2176    },
2177}
2178impl TransformGesture {
2179    fn on_input(&mut self, args: &TouchInputArgs) {
2180        match mem::take(self) {
2181            Self::NoStartedZero => {
2182                if TouchPhase::Start == args.phase
2183                    && !args.touch_propagation.is_stopped()
2184                    && (TOUCH_TRANSFORM_EVENT.has_hooks()
2185                        || args.target.widgets_path().iter().any(|w| TOUCH_TRANSFORM_EVENT.is_subscriber(*w)))
2186                {
2187                    *self = Self::NotStartedOne {
2188                        window_id: args.window_id,
2189                        device_id: args.device_id,
2190                        start_position: args.position,
2191                        position: args.position,
2192                        handle: args.touch_propagation.clone(),
2193                    }
2194                }
2195            }
2196            Self::NotStartedOne {
2197                window_id,
2198                device_id,
2199                position,
2200                handle,
2201                ..
2202            } => {
2203                if TouchPhase::Start == args.phase
2204                    && window_id == args.window_id
2205                    && device_id == args.device_id
2206                    && !args.touch_propagation.is_stopped()
2207                    && !handle.is_stopped()
2208                    && handle != args.touch_propagation
2209                {
2210                    if let Ok(w) = WINDOWS.widget_tree(args.window_id) {
2211                        *self = Self::NotStartedTwo {
2212                            window_id: args.window_id,
2213                            device_id: args.device_id,
2214                            start_position: [position, args.position],
2215                            position: [position, args.position],
2216                            handle: [handle, args.touch_propagation.clone()],
2217                            scale_factor: w.scale_factor(),
2218                        }
2219                    }
2220                }
2221            }
2222            Self::NotStartedTwo { .. } => {
2223                // cancel before start
2224            }
2225            Self::StartedOne {
2226                window_id,
2227                device_id,
2228                position,
2229                velocity,
2230                scale_factor,
2231                handle,
2232                first_info,
2233                hits,
2234                target,
2235            } => match args.phase {
2236                TouchPhase::Start
2237                    if window_id == args.window_id
2238                        && device_id == args.device_id
2239                        && !args.touch_propagation.is_stopped()
2240                        && !handle.is_stopped()
2241                        && handle != args.touch_propagation =>
2242                {
2243                    *self = Self::StartedTwo {
2244                        window_id,
2245                        device_id,
2246                        position: [position, args.position],
2247                        velocity: [velocity, args.velocity],
2248                        scale_factor,
2249                        handle: [handle, args.touch_propagation.clone()],
2250                        first_info,
2251                        hits,
2252                        target,
2253                    };
2254                }
2255                TouchPhase::Move => unreachable!(),
2256                TouchPhase::End if handle == args.touch_propagation => {
2257                    let position = args.position;
2258
2259                    let latest_info = TouchTransformInfo::new_dip([position, position], scale_factor);
2260                    let capture = POINTER_CAPTURE.current_capture_value();
2261
2262                    let velocity = velocity.to_px(scale_factor);
2263
2264                    let args = TouchTransformArgs::now(
2265                        window_id,
2266                        device_id,
2267                        first_info,
2268                        latest_info,
2269                        [velocity, velocity],
2270                        scale_factor,
2271                        TouchPhase::End,
2272                        hits,
2273                        target,
2274                        capture,
2275                        args.modifiers,
2276                    );
2277                    TOUCH_TRANSFORM_EVENT.notify(args);
2278                }
2279                _ => {
2280                    // cancel or invalid start
2281                    *self = Self::StartedOne {
2282                        window_id,
2283                        device_id,
2284                        position,
2285                        velocity,
2286                        scale_factor,
2287                        handle,
2288                        first_info,
2289                        hits,
2290                        target,
2291                    };
2292                    self.clear();
2293                }
2294            },
2295            Self::StartedTwo {
2296                window_id,
2297                device_id,
2298                mut position,
2299                velocity,
2300                scale_factor,
2301                handle,
2302                first_info,
2303                hits,
2304                target,
2305            } => {
2306                if TouchPhase::End == args.phase && handle.iter().any(|h| h == &args.touch_propagation) {
2307                    let i = handle.iter().position(|h| h == &args.touch_propagation).unwrap();
2308                    position[i] = args.position;
2309
2310                    let latest_info = TouchTransformInfo::new_dip(position, scale_factor);
2311                    let capture = POINTER_CAPTURE.current_capture_value();
2312
2313                    let velocity = [velocity[0].to_px(scale_factor), velocity[1].to_px(scale_factor)];
2314
2315                    let args = TouchTransformArgs::now(
2316                        window_id,
2317                        device_id,
2318                        first_info,
2319                        latest_info,
2320                        velocity,
2321                        scale_factor,
2322                        TouchPhase::End,
2323                        hits,
2324                        target,
2325                        capture,
2326                        args.modifiers,
2327                    );
2328                    TOUCH_TRANSFORM_EVENT.notify(args);
2329                } else {
2330                    *self = Self::StartedTwo {
2331                        window_id,
2332                        device_id,
2333                        position,
2334                        velocity,
2335                        scale_factor,
2336                        handle,
2337                        first_info,
2338                        hits,
2339                        target,
2340                    };
2341                    self.clear();
2342                }
2343            }
2344        }
2345    }
2346
2347    fn on_move(&mut self, args: &TouchMoveArgs) {
2348        match self {
2349            Self::NoStartedZero => {}
2350            Self::NotStartedOne {
2351                start_position,
2352                position,
2353                handle,
2354                window_id,
2355                device_id,
2356            } => {
2357                if handle.is_stopped() {
2358                    *self = Self::NoStartedZero;
2359                } else {
2360                    let mut moved = false;
2361                    for t in &args.touches {
2362                        if handle == &t.touch_propagation {
2363                            *position = t.position();
2364                            moved = true;
2365                        } else {
2366                            *self = Self::NoStartedZero;
2367                            return;
2368                        }
2369                    }
2370                    if moved {
2371                        let cfg = TOUCH.touch_config().get();
2372                        if (position.x - start_position.x).abs() > cfg.double_tap_area.width
2373                            || (position.y - start_position.y).abs() > cfg.double_tap_area.height
2374                        {
2375                            if let Ok(w) = WINDOWS.widget_tree(*window_id) {
2376                                let scale_factor = w.scale_factor();
2377                                let first_info = TouchTransformInfo::new_dip([*start_position, *start_position], scale_factor);
2378                                let latest_info = TouchTransformInfo::new_dip([*position, *position], scale_factor);
2379
2380                                let hits = w.root().hit_test(first_info.center.cast());
2381                                let target = hits
2382                                    .target()
2383                                    .and_then(|t| w.get(t.widget_id))
2384                                    .map(|t| t.interaction_path())
2385                                    .unwrap_or_else(|| w.root().interaction_path());
2386
2387                                let target = match target.unblocked() {
2388                                    Some(t) => t,
2389                                    None => {
2390                                        *self = Self::NoStartedZero;
2391                                        return; // entire window blocked
2392                                    }
2393                                };
2394                                let capture = POINTER_CAPTURE.current_capture_value();
2395
2396                                // takeover the gesture.
2397                                handle.stop();
2398
2399                                let args = TouchTransformArgs::now(
2400                                    *window_id,
2401                                    *device_id,
2402                                    first_info.clone(),
2403                                    latest_info,
2404                                    [PxVector::zero(); 2],
2405                                    scale_factor,
2406                                    TouchPhase::Start,
2407                                    hits.clone(),
2408                                    target.clone(),
2409                                    capture,
2410                                    args.modifiers,
2411                                );
2412                                TOUCH_TRANSFORM_EVENT.notify(args);
2413
2414                                *self = Self::StartedOne {
2415                                    window_id: *window_id,
2416                                    device_id: *device_id,
2417                                    position: *position,
2418                                    velocity: DipVector::zero(),
2419                                    scale_factor,
2420                                    handle: handle.clone(),
2421                                    first_info,
2422                                    hits,
2423                                    target,
2424                                };
2425                            } else {
2426                                *self = Self::NoStartedZero;
2427                            }
2428                        }
2429                    }
2430                }
2431            }
2432            Self::NotStartedTwo {
2433                start_position,
2434                position,
2435                handle,
2436                scale_factor,
2437                window_id,
2438                device_id,
2439            } => {
2440                if handle[0].is_stopped() || handle[1].is_stopped() {
2441                    *self = Self::NoStartedZero;
2442                } else {
2443                    let mut any_moved = false;
2444                    for t in &args.touches {
2445                        if let Some(i) = handle.iter().position(|h| h == &t.touch_propagation) {
2446                            position[i] = t.position();
2447                            any_moved = true;
2448                        } else {
2449                            *self = Self::NoStartedZero;
2450                            return;
2451                        }
2452                    }
2453
2454                    if any_moved {
2455                        let first_info = TouchTransformInfo::new_dip(*start_position, *scale_factor);
2456                        let latest_info = TouchTransformInfo::new_dip(*position, *scale_factor);
2457
2458                        let start = {
2459                            let translation = first_info.translation(&latest_info);
2460                            translation.x > 0.0 && translation.y > 0.0
2461                        } || {
2462                            let scale = first_info.scale(&latest_info);
2463                            scale.0 != 1.0
2464                        } || {
2465                            let rotate = first_info.rotation(&latest_info);
2466                            rotate.0 != 0.0
2467                        };
2468
2469                        if start {
2470                            if let Ok(w) = WINDOWS.widget_tree(*window_id) {
2471                                let hits = w.root().hit_test(first_info.center.cast());
2472                                let target = hits
2473                                    .target()
2474                                    .and_then(|t| w.get(t.widget_id))
2475                                    .map(|t| t.interaction_path())
2476                                    .unwrap_or_else(|| w.root().interaction_path());
2477
2478                                let target = match target.unblocked() {
2479                                    Some(t) => t,
2480                                    None => {
2481                                        *self = Self::NoStartedZero;
2482                                        return; // entire window blocked
2483                                    }
2484                                };
2485                                let capture = POINTER_CAPTURE.current_capture_value();
2486
2487                                for h in handle.iter() {
2488                                    // takeover the gesture.
2489                                    h.stop();
2490                                }
2491
2492                                let args = TouchTransformArgs::now(
2493                                    *window_id,
2494                                    *device_id,
2495                                    first_info.clone(),
2496                                    latest_info,
2497                                    [PxVector::zero(); 2],
2498                                    *scale_factor,
2499                                    TouchPhase::Start,
2500                                    hits.clone(),
2501                                    target.clone(),
2502                                    capture,
2503                                    args.modifiers,
2504                                );
2505                                TOUCH_TRANSFORM_EVENT.notify(args);
2506
2507                                *self = Self::StartedTwo {
2508                                    window_id: *window_id,
2509                                    device_id: *device_id,
2510                                    position: *position,
2511                                    velocity: [DipVector::zero(); 2],
2512                                    scale_factor: *scale_factor,
2513                                    handle: handle.clone(),
2514                                    first_info,
2515                                    hits,
2516                                    target,
2517                                };
2518                            } else {
2519                                *self = Self::NoStartedZero;
2520                            }
2521                        }
2522                    }
2523                }
2524            }
2525            Self::StartedOne {
2526                window_id,
2527                device_id,
2528                position,
2529                velocity,
2530                scale_factor,
2531                handle,
2532                first_info,
2533                hits,
2534                target,
2535            } => {
2536                let mut any_moved = false;
2537                for t in &args.touches {
2538                    if handle == &t.touch_propagation {
2539                        *position = t.position();
2540                        *velocity = t.velocity;
2541                        any_moved = true;
2542                    } else {
2543                        self.clear();
2544                        return;
2545                    }
2546                }
2547
2548                if any_moved {
2549                    let latest_info = TouchTransformInfo::new_dip([*position, *position], *scale_factor);
2550                    let capture = POINTER_CAPTURE.current_capture_value();
2551
2552                    let velocity = velocity.to_px(*scale_factor);
2553
2554                    let args = TouchTransformArgs::now(
2555                        *window_id,
2556                        *device_id,
2557                        first_info.clone(),
2558                        latest_info,
2559                        [velocity, velocity],
2560                        *scale_factor,
2561                        TouchPhase::Move,
2562                        hits.clone(),
2563                        target.clone(),
2564                        capture,
2565                        args.modifiers,
2566                    );
2567                    TOUCH_TRANSFORM_EVENT.notify(args);
2568                }
2569            }
2570            Self::StartedTwo {
2571                window_id,
2572                device_id,
2573                position,
2574                scale_factor,
2575                velocity,
2576                handle,
2577                first_info,
2578                hits,
2579                target,
2580            } => {
2581                let mut any_moved = false;
2582                for t in &args.touches {
2583                    if let Some(i) = handle.iter().position(|h| h == &t.touch_propagation) {
2584                        position[i] = t.position();
2585                        velocity[i] = t.velocity;
2586                        any_moved = true;
2587                    } else {
2588                        self.clear();
2589                        return;
2590                    }
2591                }
2592
2593                if any_moved {
2594                    let latest_info = TouchTransformInfo::new_dip(*position, *scale_factor);
2595                    let capture = POINTER_CAPTURE.current_capture_value();
2596
2597                    let velocity = [velocity[0].to_px(*scale_factor), velocity[1].to_px(*scale_factor)];
2598
2599                    let args = TouchTransformArgs::now(
2600                        *window_id,
2601                        *device_id,
2602                        first_info.clone(),
2603                        latest_info,
2604                        velocity,
2605                        *scale_factor,
2606                        TouchPhase::Move,
2607                        hits.clone(),
2608                        target.clone(),
2609                        capture,
2610                        args.modifiers,
2611                    );
2612                    TOUCH_TRANSFORM_EVENT.notify(args);
2613                }
2614            }
2615        }
2616    }
2617
2618    fn clear(&mut self) {
2619        match mem::take(self) {
2620            TransformGesture::StartedOne {
2621                window_id,
2622                device_id,
2623                scale_factor,
2624                first_info,
2625                hits,
2626                target,
2627                ..
2628            }
2629            | TransformGesture::StartedTwo {
2630                window_id,
2631                device_id,
2632                scale_factor,
2633                first_info,
2634                hits,
2635                target,
2636                ..
2637            } => {
2638                let args = TouchTransformArgs::now(
2639                    window_id,
2640                    device_id,
2641                    first_info.clone(),
2642                    first_info,
2643                    [PxVector::zero(); 2],
2644                    scale_factor,
2645                    TouchPhase::Cancel,
2646                    hits,
2647                    target,
2648                    None,
2649                    ModifiersState::empty(),
2650                );
2651                TOUCH_TRANSFORM_EVENT.notify(args);
2652            }
2653            _ => {}
2654        }
2655    }
2656}