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