zng_ext_input/
mouse.rs

1//! Mouse events and service.
2//!
3//! # Events
4//!
5//! Events this extension provides.
6//!
7//! * [`MOUSE_MOVE_EVENT`]
8//! * [`MOUSE_INPUT_EVENT`]
9//! * [`MOUSE_CLICK_EVENT`]
10//! * [`MOUSE_HOVERED_EVENT`]
11//!
12//! # Services
13//!
14//! Services this extension provides.
15//!
16//! * [`MOUSE`]
17
18use std::{collections::HashMap, mem, num::NonZeroU32, time::*};
19
20use zng_app::{
21    DInstant, INSTANT,
22    event::{EventPropagationHandle, event, event_args},
23    hn,
24    shortcut::ModifiersState,
25    timer::{DeadlineVar, TIMERS},
26    view_process::{
27        VIEW_PROCESS_INITED_EVENT,
28        raw_device_events::InputDeviceId,
29        raw_events::{
30            RAW_MOUSE_INPUT_EVENT, RAW_MOUSE_LEFT_EVENT, RAW_MOUSE_MOVED_EVENT, RAW_MOUSE_WHEEL_EVENT,
31            RAW_MULTI_CLICK_CONFIG_CHANGED_EVENT, RAW_WINDOW_FOCUS_EVENT,
32        },
33    },
34    widget::{
35        WidgetId,
36        info::{HitTestInfo, InteractionPath, WIDGET_TREE_CHANGED_EVENT, WidgetInfo, WidgetInfoBuilder},
37    },
38    window::WindowId,
39};
40use zng_app_context::app_local;
41use zng_ext_window::{NestedWindowWidgetInfoExt, WINDOWS};
42use zng_layout::unit::{Dip, DipPoint, DipToPx, Factor, PxPoint, PxToDip};
43use zng_state_map::{StateId, state_map, static_id};
44use zng_var::{IntoVar, Var, context_var, impl_from_and_into_var, var};
45use zng_view_api::touch::TouchPhase;
46pub use zng_view_api::{
47    config::MultiClickConfig,
48    mouse::{ButtonState, MouseButton, MouseScrollDelta},
49};
50
51use crate::{
52    keyboard::{KEYBOARD, MODIFIERS_CHANGED_EVENT},
53    pointer_capture::{CaptureInfo, CaptureMode, POINTER_CAPTURE, POINTER_CAPTURE_EVENT},
54};
55
56event_args! {
57    /// [`MOUSE_MOVE_EVENT`] arguments.
58    pub struct MouseMoveArgs {
59        /// Id of window that received the event.
60        pub window_id: WindowId,
61
62        /// Id of device that generated the event.
63        pub device_id: InputDeviceId,
64
65        /// What modifier keys where pressed when this event happened.
66        pub modifiers: ModifiersState,
67
68        /// Positions of the cursor in between the previous event and this one.
69        ///
70        /// Mouse move events can be coalesced, i.e. multiple moves packed into a single event.
71        pub coalesced_pos: Vec<DipPoint>,
72
73        /// Position of the mouse in the window's content area.
74        pub position: DipPoint,
75
76        /// Hit-test result for the mouse point in the window.
77        pub hits: HitTestInfo,
78
79        /// Full path to the top-most hit in [`hits`](MouseMoveArgs::hits).
80        pub target: InteractionPath,
81
82        /// Current pointer capture.
83        pub capture: Option<CaptureInfo>,
84
85        ..
86
87        /// if is in [`target`] or [`capture`].
88        ///
89        /// [`target`]: Self::target
90        /// [`capture`]: Self::capture
91        fn is_in_target(&self, id: WidgetId) -> bool {
92            if self.target.contains(id) {
93                return true;
94            }
95
96            if let Some(c) = &self.capture {
97                return c.target.contains(id);
98            }
99
100            false
101        }
102    }
103
104    /// [`MOUSE_INPUT_EVENT`] arguments.
105    pub struct MouseInputArgs {
106        /// Id of window that received the event.
107        pub window_id: WindowId,
108
109        /// Id of device that generated the event.
110        pub device_id: Option<InputDeviceId>,
111
112        /// Which mouse button generated the event.
113        pub button: MouseButton,
114
115        /// Position of the mouse in the window's content area.
116        pub position: DipPoint,
117
118        /// What modifier keys where pressed when this event happened.
119        pub modifiers: ModifiersState,
120
121        /// The state the [`button`] was changed to.
122        ///
123        /// [`button`]: Self::button
124        pub state: ButtonState,
125
126        /// Hit-test result for the mouse point in the window.
127        pub hits: HitTestInfo,
128
129        /// Full path to the top-most hit in [`hits`].
130        ///
131        /// [`hits`]: Self::hits
132        pub target: InteractionPath,
133
134        /// Current pointer capture.
135        pub capture: Option<CaptureInfo>,
136
137        /// If [`MOUSE_CLICK_EVENT`] will notify because of this input.
138        ///
139        /// The click event shares the same propagation handle as this event.
140        pub is_click: bool,
141
142        ..
143
144        /// If is in [`target`] or [`capture`].
145        ///
146        /// [`target`]: Self::target
147        /// [`capture`]: Self::capture
148        fn is_in_target(&self, id: WidgetId) -> bool {
149            if self.target.contains(id) {
150                return true;
151            }
152
153            if let Some(c) = &self.capture {
154                return c.target.contains(id);
155            }
156
157            false
158        }
159    }
160
161    /// [`MOUSE_CLICK_EVENT`] arguments.
162    pub struct MouseClickArgs {
163        /// Id of window that received the event.
164        pub window_id: WindowId,
165
166        /// Id of device that generated the event.
167        pub device_id: InputDeviceId,
168
169        /// Which mouse button generated the event.
170        pub button: MouseButton,
171
172        /// Position of the mouse in the coordinates of [`target`](MouseClickArgs::target).
173        pub position: DipPoint,
174
175        /// What modifier keys where pressed when this event happened.
176        pub modifiers: ModifiersState,
177
178        /// Count of clicks within the double-click interval. Number `1` is single click, `2` is double click, etc.
179        ///
180        /// Auto repeated clicks also increment this count.
181        pub click_count: NonZeroU32,
182
183        /// If the event was generated by holding the button pressed over a widget with [`ClickMode::repeat`].
184        pub is_repeat: bool,
185
186        /// Hit-test result for the mouse point in the window, at the moment the click event
187        /// was generated.
188        pub hits: HitTestInfo,
189
190        /// Full path to the widget that got clicked.
191        ///
192        /// A widget is clicked if the [mouse down] and [mouse up] happen
193        /// in sequence in the same widget. Subsequent clicks (double, triple)
194        /// happen on mouse down.
195        ///
196        /// If a [mouse down] happen in a child widget and the pointer is dragged
197        /// to a larger parent widget and then let go (mouse up), the click target
198        /// is the parent widget.
199        ///
200        /// Multi-clicks ([`click_count`] > 1) only happen to the same target.
201        ///
202        /// [mouse down]: MouseInputArgs::is_mouse_down
203        /// [mouse up]: MouseInputArgs::is_mouse_up
204        /// [`click_count`]: MouseClickArgs::click_count
205        pub target: InteractionPath,
206
207        ..
208
209        /// If is in [`target`].
210        ///
211        /// [`target`]: MouseClickArgs::target
212        fn is_in_target(&self, id: WidgetId) -> bool {
213            self.target.contains(id)
214        }
215    }
216
217    /// [`MOUSE_HOVERED_EVENT`] arguments.
218    pub struct MouseHoverArgs {
219        /// Id of window that received the event.
220        pub window_id: WindowId,
221
222        /// Id of device that generated the event.
223        pub device_id: Option<InputDeviceId>,
224
225        /// Position of the mouse in the window.
226        pub position: DipPoint,
227
228        /// Hit-test result for the mouse point in the window.
229        pub hits: HitTestInfo,
230
231        /// Previous top-most hit before the mouse moved.
232        pub prev_target: Option<InteractionPath>,
233
234        /// Full path to the top-most hit in [`hits`].
235        ///
236        /// Is `None` when the mouse moves out of a window or the window closes under the mouse
237        /// and there was a previous hovered widget.
238        ///
239        /// [`hits`]: MouseInputArgs::hits
240        pub target: Option<InteractionPath>,
241
242        /// Previous pointer capture.
243        pub prev_capture: Option<CaptureInfo>,
244
245        /// Current pointer capture.
246        pub capture: Option<CaptureInfo>,
247
248        ..
249
250        /// If is in [`target`], [`prev_target`] or [`capture`].
251        ///
252        /// [`target`]: Self::target
253        /// [`prev_target`]: Self::prev_target
254        /// [`capture`]: Self::capture
255        fn is_in_target(&self, id: WidgetId) -> bool {
256            if let Some(t) = &self.target
257                && t.contains(id)
258            {
259                return true;
260            }
261            if let Some(t) = &self.prev_target
262                && t.contains(id)
263            {
264                return true;
265            }
266
267            if let Some(c) = &self.capture {
268                return c.target.contains(id);
269            }
270
271            false
272        }
273    }
274
275    /// [`MOUSE_WHEEL_EVENT`] arguments.
276    pub struct MouseWheelArgs {
277        /// Id of window that received the event.
278        pub window_id: WindowId,
279        /// Id of device that generated the event.
280        pub device_id: InputDeviceId,
281
282        /// Position of the mouse in the coordinates of [`target`](MouseWheelArgs::target).
283        pub position: DipPoint,
284        /// What modifier keys where pressed when this event happened.
285        pub modifiers: ModifiersState,
286
287        /// Wheel motion delta, value is in pixels if the *wheel* is a touchpad.
288        pub delta: MouseScrollDelta,
289
290        /// Touch state if the device that generated the event is a touchpad.
291        pub phase: TouchPhase,
292
293        /// Hit-test result for the mouse point in the window, at the moment the wheel event
294        /// was generated.
295        pub hits: HitTestInfo,
296
297        /// Full path to the widget that got scrolled.
298        pub target: InteractionPath,
299
300        ..
301
302        /// If is in [`target`].
303        ///
304        /// [`target`]: MouseWheelArgs::target
305        fn is_in_target(&self, id: WidgetId) -> bool {
306            self.target.contains(id)
307        }
308    }
309}
310
311impl MouseHoverArgs {
312    /// If [`capture`] is `None` or [`allows`] the `wgt` to receive this event.
313    ///
314    /// [`capture`]: Self::capture
315    /// [`allows`]: CaptureInfo::allows
316    pub fn capture_allows(&self, wgt: (WindowId, WidgetId)) -> bool {
317        self.capture.as_ref().map(|c| c.allows(wgt)).unwrap_or(true)
318    }
319
320    /// Event caused by the mouse position moving over/out of the widget bounds.
321    pub fn is_mouse_move(&self) -> bool {
322        self.device_id.is_some()
323    }
324
325    /// Event caused by the widget moving under/out of the mouse position.
326    pub fn is_widget_move(&self) -> bool {
327        self.device_id.is_none()
328    }
329
330    /// Event caused by a pointer capture change.
331    pub fn is_capture_change(&self) -> bool {
332        self.prev_capture != self.capture
333    }
334
335    /// Returns `true` if the `wgt` was not hovered, but now is.
336    pub fn is_mouse_enter(&self, wgt: (WindowId, WidgetId)) -> bool {
337        !self.was_over(wgt) && self.is_over(wgt)
338    }
339
340    /// Returns `true` if the `wgt` was hovered, but now isn't.
341    pub fn is_mouse_leave(&self, wgt: (WindowId, WidgetId)) -> bool {
342        self.was_over(wgt) && !self.is_over(wgt)
343    }
344
345    /// Returns `true` if the `wgt` was not hovered or was disabled, but now is hovered and enabled.
346    pub fn is_mouse_enter_enabled(&self, wgt: (WindowId, WidgetId)) -> bool {
347        (!self.was_over(wgt) || self.was_disabled(wgt.1)) && self.is_over(wgt) && self.is_enabled(wgt.1)
348    }
349
350    /// Returns `true` if the `wgt` was hovered and enabled, but now is not hovered or is disabled.
351    pub fn is_mouse_leave_enabled(&self, wgt: (WindowId, WidgetId)) -> bool {
352        self.was_over(wgt) && self.was_enabled(wgt.1) && (!self.is_over(wgt) || self.is_disabled(wgt.1))
353    }
354
355    /// Returns `true` if the `wgt` was not hovered or was enabled, but now is hovered and disabled.
356    pub fn is_mouse_enter_disabled(&self, wgt: (WindowId, WidgetId)) -> bool {
357        (!self.was_over(wgt) || self.was_enabled(wgt.1)) && self.is_over(wgt) && self.is_disabled(wgt.1)
358    }
359
360    /// Returns `true` if the `wgt` was hovered and disabled, but now is not hovered or is enabled.
361    pub fn is_mouse_leave_disabled(&self, wgt: (WindowId, WidgetId)) -> bool {
362        self.was_over(wgt) && self.was_disabled(wgt.1) && (!self.is_over(wgt) || self.is_enabled(wgt.1))
363    }
364
365    /// Returns `true` if the `wgt` is in [`prev_target`] and is allowed by the [`prev_capture`].
366    ///
367    /// [`prev_target`]: Self::prev_target
368    /// [`prev_capture`]: Self::prev_capture
369    pub fn was_over(&self, wgt: (WindowId, WidgetId)) -> bool {
370        if let Some(cap) = &self.prev_capture
371            && !cap.allows(wgt)
372        {
373            return false;
374        }
375
376        if let Some(t) = &self.prev_target {
377            return t.contains(wgt.1);
378        }
379
380        false
381    }
382
383    /// Returns `true` if the `wgt` is in [`target`] and is allowed by the current [`capture`].
384    ///
385    /// [`target`]: Self::target
386    /// [`capture`]: Self::capture
387    pub fn is_over(&self, wgt: (WindowId, WidgetId)) -> bool {
388        if let Some(cap) = &self.capture
389            && !cap.allows(wgt)
390        {
391            return false;
392        }
393
394        if let Some(t) = &self.target {
395            return t.contains(wgt.1);
396        }
397
398        false
399    }
400
401    /// Returns `true` if the widget was enabled in [`prev_target`].
402    ///
403    /// [`prev_target`]: Self::prev_target
404    pub fn was_enabled(&self, widget_id: WidgetId) -> bool {
405        match &self.prev_target {
406            Some(t) => t.contains_enabled(widget_id),
407            None => false,
408        }
409    }
410
411    /// Returns `true` if the widget was disabled in [`prev_target`].
412    ///
413    /// [`prev_target`]: Self::prev_target
414    pub fn was_disabled(&self, widget_id: WidgetId) -> bool {
415        match &self.prev_target {
416            Some(t) => t.contains_disabled(widget_id),
417            None => false,
418        }
419    }
420
421    /// Returns `true` if the widget is enabled in [`target`].
422    ///
423    /// [`target`]: Self::target
424    pub fn is_enabled(&self, widget_id: WidgetId) -> bool {
425        match &self.target {
426            Some(t) => t.contains_enabled(widget_id),
427            None => false,
428        }
429    }
430
431    /// Returns `true` if the widget is disabled in [`target`].
432    ///
433    /// [`target`]: Self::target
434    pub fn is_disabled(&self, widget_id: WidgetId) -> bool {
435        match &self.target {
436            Some(t) => t.contains_disabled(widget_id),
437            None => false,
438        }
439    }
440
441    /// Gets position in the widget inner bounds.
442    pub fn position_wgt(&self, wgt: (WindowId, WidgetId)) -> Option<PxPoint> {
443        let tree = WINDOWS.widget_tree(wgt.0)?;
444        tree.get(wgt.1)?
445            .inner_transform()
446            .inverse()?
447            .transform_point(self.position.to_px(tree.scale_factor()))
448    }
449}
450
451impl MouseMoveArgs {
452    /// If [`capture`] is `None` or [`allows`] the `wgt` to receive this event.
453    ///
454    /// [`capture`]: Self::capture
455    /// [`allows`]: CaptureInfo::allows
456    pub fn capture_allows(&self, wgt: (WindowId, WidgetId)) -> bool {
457        self.capture.as_ref().map(|c| c.allows(wgt)).unwrap_or(true)
458    }
459
460    /// Gets position in the widget inner bounds.
461    pub fn position_wgt(&self, wgt: (WindowId, WidgetId)) -> Option<PxPoint> {
462        let tree = WINDOWS.widget_tree(wgt.0)?;
463        tree.get(wgt.1)?
464            .inner_transform()
465            .inverse()?
466            .transform_point(self.position.to_px(tree.scale_factor()))
467    }
468}
469
470impl MouseInputArgs {
471    /// If [`capture`] is `None` or [`allows`] the `wgt` to receive this event.
472    ///
473    /// [`capture`]: Self::capture
474    /// [`allows`]: CaptureInfo::allows
475    pub fn capture_allows(&self, wgt: (WindowId, WidgetId)) -> bool {
476        self.capture.as_ref().map(|c| c.allows(wgt)).unwrap_or(true)
477    }
478
479    /// If the `widget_id` is in the [`target`].
480    ///
481    /// [`target`]: Self::target
482    pub fn is_over(&self, widget_id: WidgetId) -> bool {
483        self.target.contains(widget_id)
484    }
485
486    /// If the [`button`] is the primary.
487    ///
488    /// [`button`]: Self::button
489    pub fn is_primary(&self) -> bool {
490        self.button == MouseButton::Left
491    }
492
493    /// If the [`button`](Self::button) is the context (right).
494    pub fn is_context(&self) -> bool {
495        self.button == MouseButton::Right
496    }
497
498    /// If the [`state`] is pressed.
499    ///
500    /// [`state`]: Self::state
501    pub fn is_mouse_down(&self) -> bool {
502        self.state == ButtonState::Pressed
503    }
504
505    /// If the [`state`] is released.
506    ///
507    /// [`state`]: Self::state
508    pub fn is_mouse_up(&self) -> bool {
509        self.state == ButtonState::Released
510    }
511
512    /// Gets position in the widget inner bounds.
513    pub fn position_wgt(&self, wgt: (WindowId, WidgetId)) -> Option<PxPoint> {
514        let tree = WINDOWS.widget_tree(wgt.0)?;
515        tree.get(wgt.1)?
516            .inner_transform()
517            .inverse()?
518            .transform_point(self.position.to_px(tree.scale_factor()))
519    }
520}
521
522impl MouseClickArgs {
523    /// If the [`button`](Self::button) is the primary.
524    pub fn is_primary(&self) -> bool {
525        self.button == MouseButton::Left
526    }
527
528    /// If the [`button`](Self::button) is the context (right).
529    pub fn is_context(&self) -> bool {
530        self.button == MouseButton::Right
531    }
532
533    /// If the [`click_count`](Self::click_count) is `1`.
534    pub fn is_single(&self) -> bool {
535        self.click_count.get() == 1
536    }
537
538    /// If the [`click_count`](Self::click_count) is `2`.
539    pub fn is_double(&self) -> bool {
540        self.click_count.get() == 2
541    }
542
543    /// If the [`click_count`](Self::click_count) is `3`.
544    pub fn is_triple(&self) -> bool {
545        self.click_count.get() == 3
546    }
547
548    /// Gets position in the widget inner bounds.
549    pub fn position_wgt(&self, wgt: (WindowId, WidgetId)) -> Option<PxPoint> {
550        let tree = WINDOWS.widget_tree(wgt.0)?;
551        tree.get(wgt.1)?
552            .inner_transform()
553            .inverse()?
554            .transform_point(self.position.to_px(tree.scale_factor()))
555    }
556}
557
558impl MouseWheelArgs {
559    /// Swaps the delta axis if [`modifiers`] contains `SHIFT`.
560    ///
561    /// [`modifiers`]: Self::modifiers
562    pub fn shifted_delta(&self) -> MouseScrollDelta {
563        if self.modifiers.has_shift() {
564            match self.delta {
565                MouseScrollDelta::LineDelta(x, y) => MouseScrollDelta::LineDelta(y, x),
566                MouseScrollDelta::PixelDelta(x, y) => MouseScrollDelta::PixelDelta(y, x),
567                _ => unimplemented!(),
568            }
569        } else {
570            self.delta
571        }
572    }
573
574    /// If the modifiers allow the event to be used for scrolling.
575    ///
576    /// Is `true` if only `SHIFT`, `ALT` or none modifiers are pressed. If `true` the
577    /// [`scroll_delta`] method returns a value.
578    ///
579    /// If contextual [`CTRL_SCROLL_VAR`] is `true` the `CTRL` modifier must be pressed for this to be `true`.
580    ///
581    /// [`scroll_delta`]: Self::scroll_delta
582    pub fn is_scroll(&self) -> bool {
583        if CTRL_SCROLL_VAR.get() {
584            self.modifiers
585                .is_only(ModifiersState::CTRL | ModifiersState::SHIFT | ModifiersState::ALT)
586        } else {
587            self.modifiers.is_empty() || self.modifiers.is_only(ModifiersState::SHIFT | ModifiersState::ALT)
588        }
589    }
590
591    /// Returns the delta for a scrolling operation, depending on the [`modifiers`].
592    ///
593    /// If [`is_scroll`] scales the delta by `alt_factor`, then, if no more modifiers are pressed returns
594    /// the scaled delta, if only `SHIFT` is pressed returns the swapped delta, otherwise returns `None`.
595    ///
596    /// [`is_scroll`]: Self::is_scroll
597    /// [`modifiers`]: Self::modifiers
598    pub fn scroll_delta(&self, alt_factor: impl Into<Factor>) -> Option<MouseScrollDelta> {
599        let mut modifiers = self.modifiers;
600        if CTRL_SCROLL_VAR.get() && !modifiers.take_ctrl() {
601            return None;
602        }
603        let mut delta = self.delta;
604        if modifiers.take_alt() {
605            let alt_factor = alt_factor.into();
606            delta = match delta {
607                MouseScrollDelta::LineDelta(x, y) => MouseScrollDelta::LineDelta(x * alt_factor.0, y * alt_factor.0),
608                MouseScrollDelta::PixelDelta(x, y) => MouseScrollDelta::PixelDelta(x * alt_factor.0, y * alt_factor.0),
609                _ => return None,
610            };
611        }
612
613        if modifiers.is_empty() {
614            Some(delta)
615        } else if modifiers.is_only_shift() {
616            Some(match delta {
617                MouseScrollDelta::LineDelta(x, y) => MouseScrollDelta::LineDelta(y, x),
618                MouseScrollDelta::PixelDelta(x, y) => MouseScrollDelta::PixelDelta(y, x),
619                _ => return None,
620            })
621        } else {
622            None
623        }
624    }
625
626    /// If the modifiers allow the event to be used for zooming.
627    ///
628    /// Is `true` if only `CTRL` is pressed. If `true` the [`zoom_delta`] method returns a value.
629    ///
630    /// If contextual [`CTRL_SCROLL_VAR`] is `true` no modifiers must be pressed for this to be `true`.
631    ///
632    /// [`zoom_delta`]: Self::zoom_delta
633    pub fn is_zoom(&self) -> bool {
634        if CTRL_SCROLL_VAR.get() {
635            self.modifiers.is_empty()
636        } else {
637            self.modifiers.is_only_ctrl()
638        }
639    }
640
641    /// Returns the delta for a zoom-in/out operation, depending on the [`modifiers`].
642    ///
643    /// If [`is_zoom`] returns the scroll delta, otherwise returns `None`.
644    ///
645    /// [`is_zoom`]: Self::is_zoom
646    /// [`modifiers`]: Self::modifiers
647    pub fn zoom_delta(&self) -> Option<MouseScrollDelta> {
648        if self.is_zoom() { Some(self.delta) } else { None }
649    }
650
651    /// Gets position in the widget inner bounds.
652    pub fn position_wgt(&self, wgt: (WindowId, WidgetId)) -> Option<PxPoint> {
653        let tree = WINDOWS.widget_tree(wgt.0)?;
654        tree.get(wgt.1)?
655            .inner_transform()
656            .inverse()?
657            .transform_point(self.position.to_px(tree.scale_factor()))
658    }
659}
660
661context_var! {
662    /// Defines if [`MouseWheelArgs`] gesture is [`is_scroll`] when `CTRL` is pressed and [`is_zoom`] when no modifier is pressed.
663    ///
664    /// Is `false` by default.
665    ///
666    /// [`is_scroll`]: MouseWheelArgs::is_scroll
667    /// [`is_zoom`]: MouseWheelArgs::is_zoom
668    pub static CTRL_SCROLL_VAR: bool = false;
669}
670
671event! {
672    /// Mouse move event.
673    pub static MOUSE_MOVE_EVENT: MouseMoveArgs {
674        let _ = MOUSE_SV.read();
675    };
676
677    /// Mouse down or up event.
678    pub static MOUSE_INPUT_EVENT: MouseInputArgs {
679        let _ = MOUSE_SV.read();
680    };
681
682    /// Mouse click event, any [`click_count`](MouseClickArgs::click_count).
683    pub static MOUSE_CLICK_EVENT: MouseClickArgs {
684        let _ = MOUSE_SV.read();
685    };
686
687    /// The top-most hovered widget changed or pointer capture changed.
688    pub static MOUSE_HOVERED_EVENT: MouseHoverArgs {
689        let _ = MOUSE_SV.read();
690    };
691
692    /// Mouse wheel scroll event.
693    pub static MOUSE_WHEEL_EVENT: MouseWheelArgs {
694        let _ = MOUSE_SV.read();
695    };
696}
697
698/// Represents mouse gestures that can initiate a click.
699#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
700pub enum ClickTrigger {
701    /// Widget is clicked when the same mouse button is pressed and released on it.
702    PressRelease,
703    /// Widget is clicked when a mouse button is pressed on it.
704    Press,
705    /// Widget is clicked when a mouse button is released on it, even if not pressed on it.
706    Release,
707}
708
709/// Defines how click events are generated for a widget.
710#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
711pub struct ClickMode {
712    /// Gesture that causes the *first* click, that is a click that is not in the double-click interval.
713    pub single: ClickTrigger,
714
715    /// Gesture that causes the subsequent clicks, if done within the double-click interval.
716    pub double: ClickTrigger,
717
718    /// If a mouse button is held pressed after a delay generate repeat clicks on an interval.
719    pub repeat: bool,
720}
721impl Default for ClickMode {
722    /// First `PressRelease`, double `Press` and no repeat.
723    fn default() -> Self {
724        Self {
725            single: ClickTrigger::PressRelease,
726            double: ClickTrigger::Press,
727            repeat: false,
728        }
729    }
730}
731impl IntoVar<Option<ClickMode>> for ClickTrigger {
732    fn into_var(self) -> Var<Option<ClickMode>> {
733        Some(ClickMode::from(self)).into_var()
734    }
735}
736impl_from_and_into_var! {
737    fn from(gesture: ClickTrigger) -> ClickMode {
738        ClickMode {
739            single: gesture,
740            double: gesture,
741            repeat: false,
742        }
743    }
744
745    fn from(some: ClickMode) -> Option<ClickMode>;
746}
747impl ClickMode {
748    /// Click on [`ClickTrigger::Press`].
749    pub fn press() -> Self {
750        Self {
751            single: ClickTrigger::Press,
752            double: ClickTrigger::Press,
753            repeat: false,
754        }
755    }
756
757    /// Click on release.
758    pub fn release() -> Self {
759        Self {
760            single: ClickTrigger::Release,
761            double: ClickTrigger::Release,
762            repeat: false,
763        }
764    }
765
766    /// Click on press and repeat.
767    pub fn repeat() -> Self {
768        Self {
769            single: ClickTrigger::Press,
770            double: ClickTrigger::Press,
771            repeat: true,
772        }
773    }
774
775    /// Click on press+release or repeat.
776    pub fn mixed_repeat() -> Self {
777        Self {
778            single: ClickTrigger::PressRelease,
779            double: ClickTrigger::Press,
780            repeat: true,
781        }
782    }
783}
784
785/// Mouse config methods.
786pub trait WidgetInfoMouseExt {
787    /// Gets the click mode of the widget.
788    fn click_mode(&self) -> ClickMode;
789}
790impl WidgetInfoMouseExt for WidgetInfo {
791    fn click_mode(&self) -> ClickMode {
792        for w in self.self_and_ancestors() {
793            if let Some(m) = w.meta().get_clone(*CLICK_MODE_ID).flatten() {
794                return m;
795            }
796        }
797        ClickMode::default()
798    }
799}
800
801/// Mouse config builder methods.
802pub trait WidgetInfoBuilderMouseExt {
803    /// Sets the click mode of the widget.
804    ///
805    /// Setting this to `None` will cause the widget to inherit the click mode.
806    fn set_click_mode(&mut self, mode: Option<ClickMode>);
807}
808impl WidgetInfoBuilderMouseExt for WidgetInfoBuilder {
809    fn set_click_mode(&mut self, mode: Option<ClickMode>) {
810        self.with_meta(|mut m| match m.entry(*CLICK_MODE_ID) {
811            state_map::StateMapEntry::Occupied(mut e) => *e.get_mut() = mode,
812            state_map::StateMapEntry::Vacant(e) => {
813                if mode.is_some() {
814                    e.insert(mode);
815                }
816            }
817        })
818    }
819}
820
821static_id! {
822    static ref CLICK_MODE_ID: StateId<Option<ClickMode>>;
823}
824
825/// Settings that define the mouse button pressed repeat.
826#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
827pub struct ButtonRepeatConfig {
828    /// Delay before repeat starts.
829    pub start_delay: Duration,
830    /// Delay before each repeat event after the first.
831    pub interval: Duration,
832}
833impl Default for ButtonRepeatConfig {
834    /// 600ms, 100ms.
835    fn default() -> Self {
836        Self {
837            start_delay: Duration::from_millis(600),
838            interval: Duration::from_millis(100),
839        }
840    }
841}
842
843/// Mouse service.
844///
845/// # Mouse Capture
846///
847/// Mouse capture is integrated with touch capture in the [`POINTER_CAPTURE`] service.
848///
849/// [`POINTER_CAPTURE`]: crate::pointer_capture::POINTER_CAPTURE
850pub struct MOUSE;
851impl MOUSE {
852    /// Returns a read-only variable that tracks the [buttons] that are currently pressed.
853    ///
854    /// [buttons]: MouseButton
855    pub fn buttons(&self) -> Var<Vec<MouseButton>> {
856        MOUSE_SV.read().buttons.read_only()
857    }
858
859    /// Variable that defines the click-count increment time and area, a.k.a. the double-click config.
860    ///
861    /// Repeated clicks with an interval less then this time and within the distance of the first click increment the click count.
862    ///
863    /// The value is the same as [`sys_multi_click_config`], if set the variable disconnects from system config.
864    ///
865    /// [`sys_multi_click_config`]: Self::sys_multi_click_config
866    pub fn multi_click_config(&self) -> Var<MultiClickConfig> {
867        MOUSE_SV.read().multi_click_config.clone()
868    }
869
870    /// Variable that tracks the system click-count increment time and area, a.k.a. the double-click config.
871    ///
872    /// # Value Source
873    ///
874    /// The value comes from the operating system settings, the variable
875    /// updates with a new value if the system setting is changed and on view-process (re)init.
876    ///
877    /// In headless apps the default is [`MultiClickConfig::default`] and does not change.
878    pub fn sys_multi_click_config(&self) -> Var<MultiClickConfig> {
879        MOUSE_SV.read().sys_multi_click_config.read_only()
880    }
881
882    /// Variable that gets and sets the config for [`ClickMode::repeat`] clicks.
883    ///
884    /// Note that this variable is linked with [`KEYBOARD.repeat_config`] until it is set, so if it is never set
885    /// it will update with the keyboard value.
886    ///
887    /// [`KEYBOARD.repeat_config`]: KEYBOARD::repeat_config
888    pub fn repeat_config(&self) -> Var<ButtonRepeatConfig> {
889        MOUSE_SV.read().repeat_config.clone()
890    }
891
892    /// Variable that gets current hovered window and cursor point over that window.
893    pub fn position(&self) -> Var<Option<MousePosition>> {
894        MOUSE_SV.read().position.read_only()
895    }
896
897    /// Variable that gets the current hovered window and widgets.
898    pub fn hovered(&self) -> Var<Option<InteractionPath>> {
899        MOUSE_SV.read().hovered.read_only()
900    }
901}
902
903/// Mouse cursor position.
904///
905/// Tracked in [`MOUSE.position`].
906///
907/// [`MOUSE.position`]: MOUSE::position
908#[derive(Debug, Clone, Copy, PartialEq, Eq)]
909pub struct MousePosition {
910    /// Window the mouse is hovering.
911    pub window_id: WindowId,
912    /// Mouse position in the window.
913    pub position: DipPoint,
914    /// Timestamp of the mouse move.
915    pub timestamp: DInstant,
916}
917
918app_local! {
919    static MOUSE_SV: MouseService = {
920        hooks();
921        let sys_multi_click_config = var(MultiClickConfig::default());
922        MouseService {
923            multi_click_config: sys_multi_click_config.cow(),
924            sys_multi_click_config,
925            repeat_config: KEYBOARD
926                .repeat_config()
927                .map(|c| ButtonRepeatConfig {
928                    start_delay: c.start_delay,
929                    interval: c.interval,
930                })
931                .cow(),
932            buttons: var(vec![]),
933            hovered: var(None),
934            position: var(None),
935
936            pos: DipPoint::zero(),
937            pos_window: None,
938            pos_device: None,
939            hits: None,
940
941            modifiers: ModifiersState::default(),
942
943            hovered_value: None,
944            clicking: HashMap::default(),
945        }
946    };
947}
948struct MouseService {
949    multi_click_config: Var<MultiClickConfig>,
950    sys_multi_click_config: Var<MultiClickConfig>,
951    repeat_config: Var<ButtonRepeatConfig>,
952    buttons: Var<Vec<MouseButton>>,
953    hovered: Var<Option<InteractionPath>>,
954    position: Var<Option<MousePosition>>,
955
956    // last cursor move position (scaled).
957    pos: DipPoint,
958    // last cursor move over `pos_window` and source device.
959    pos_window: Option<WindowId>,
960    pos_device: Option<InputDeviceId>,
961    // last cursor move hit-test (on the pos_window or a nested window).
962    hits: Option<HitTestInfo>,
963
964    /// last modifiers.
965    modifiers: ModifiersState,
966
967    hovered_value: Option<InteractionPath>,
968    clicking: HashMap<MouseButton, ClickingInfo>,
969}
970
971struct ClickingInfo {
972    path: InteractionPath,
973    press_stop_handle: EventPropagationHandle,
974
975    pressed: bool,
976    last_pos: DipPoint,
977    last_click: DInstant,
978    click_count: u32,
979
980    repeat_timer: Option<DeadlineVar>,
981    repeat_count: u32,
982}
983
984fn hooks() {
985    WIDGET_TREE_CHANGED_EVENT
986        .hook(|args| {
987            MOUSE_SV.write().continue_hovered(args.tree.window_id());
988            true
989        })
990        .perm();
991
992    RAW_MOUSE_MOVED_EVENT
993        .hook(|args| {
994            MOUSE_SV
995                .write()
996                .on_cursor_moved(args.window_id, args.device_id, args.coalesced_pos.clone(), args.position);
997            true
998        })
999        .perm();
1000
1001    RAW_MOUSE_WHEEL_EVENT
1002        .hook(|args| {
1003            MOUSE_SV.read().on_scroll(args.window_id, args.device_id, args.delta, args.phase);
1004            true
1005        })
1006        .perm();
1007
1008    RAW_MOUSE_INPUT_EVENT
1009        .hook(|args| {
1010            MOUSE_SV
1011                .write()
1012                .on_mouse_input(args.window_id, args.device_id, args.state, args.button);
1013            true
1014        })
1015        .perm();
1016
1017    MODIFIERS_CHANGED_EVENT
1018        .hook(|args| {
1019            MOUSE_SV.write().modifiers = args.modifiers;
1020            true
1021        })
1022        .perm();
1023
1024    RAW_MOUSE_LEFT_EVENT
1025        .hook(|args| {
1026            MOUSE_SV.write().on_cursor_left_window(args.window_id, args.device_id);
1027            true
1028        })
1029        .perm();
1030
1031    RAW_WINDOW_FOCUS_EVENT
1032        .hook(|args| {
1033            if let Some(window_id) = args.prev_focus {
1034                MOUSE_SV.write().on_window_blur(window_id, args.new_focus);
1035            }
1036            true
1037        })
1038        .perm();
1039
1040    RAW_MULTI_CLICK_CONFIG_CHANGED_EVENT
1041        .hook(|args| {
1042            let mut s = MOUSE_SV.write();
1043            s.multi_click_config.set(args.config);
1044            s.clicking.clear();
1045            true
1046        })
1047        .perm();
1048
1049    VIEW_PROCESS_INITED_EVENT
1050        .hook(|args| {
1051            if args.is_respawn {
1052                MOUSE_SV.write().clean_all_state();
1053            }
1054            true
1055        })
1056        .perm();
1057
1058    POINTER_CAPTURE_EVENT
1059        .on_event(
1060            true,
1061            hn!(|a| {
1062                MOUSE_SV.write().on_pointer_capture(&a.prev_capture, &a.new_capture);
1063            }),
1064        )
1065        .perm();
1066}
1067impl MouseService {
1068    fn on_mouse_input(&mut self, mut window_id: WindowId, device_id: InputDeviceId, state: ButtonState, button: MouseButton) {
1069        let mut position = if self.pos_window == Some(window_id) {
1070            self.pos
1071        } else {
1072            DipPoint::default()
1073        };
1074
1075        let hits = self.hits.clone().unwrap_or_else(|| HitTestInfo::no_hits(window_id));
1076
1077        let wgt_tree = match WINDOWS.widget_tree(hits.window_id()) {
1078            Some(t) => t,
1079            None => {
1080                tracing::error!("cannot find clicked window, {}", hits.window_id());
1081                return;
1082            }
1083        };
1084
1085        if hits.window_id() != window_id {
1086            // is over nested window
1087            window_id = hits.window_id();
1088            position = hits.point().to_dip(wgt_tree.scale_factor());
1089        }
1090
1091        let (wgt_path, click_mode) = hits
1092            .target()
1093            .and_then(|t| wgt_tree.get(t.widget_id).map(|w| (w.interaction_path(), w.click_mode())))
1094            .unwrap_or_else(|| (wgt_tree.root().interaction_path(), wgt_tree.root().click_mode()));
1095
1096        let wgt_path = match wgt_path.unblocked() {
1097            Some(p) => p,
1098            None => return, // entire window blocked
1099        };
1100
1101        match state {
1102            ButtonState::Pressed => {
1103                self.buttons.modify(move |btns| {
1104                    if !btns.contains(&button) {
1105                        btns.push(button);
1106                    }
1107                });
1108            }
1109            ButtonState::Released => {
1110                self.buttons.modify(move |btns| {
1111                    if let Some(i) = btns.iter().position(|k| *k == button) {
1112                        btns.swap_remove(i);
1113                    }
1114                });
1115            }
1116        }
1117
1118        let stop_handle = EventPropagationHandle::new();
1119
1120        let entry = self.clicking.entry(button).or_insert_with(|| ClickingInfo {
1121            path: wgt_path.clone(),
1122            press_stop_handle: stop_handle.clone(),
1123            last_click: DInstant::EPOCH,
1124            last_pos: position,
1125            pressed: false,
1126            click_count: 0,
1127            repeat_timer: None,
1128            repeat_count: 0,
1129        });
1130
1131        if entry.path != wgt_path {
1132            let actual_change = entry.path.as_path() != wgt_path.as_path();
1133            // else only interactivity change mid-click
1134
1135            entry.path = wgt_path.clone();
1136            if actual_change {
1137                entry.press_stop_handle = stop_handle.clone();
1138                entry.pressed = false;
1139                entry.click_count = 0;
1140                entry.repeat_timer = None;
1141                entry.repeat_count = 0;
1142            }
1143        }
1144
1145        let multi_click_cfg = self.multi_click_config.get();
1146
1147        let double_allowed = entry.last_click.elapsed() <= multi_click_cfg.time && {
1148            let dist = (entry.last_pos.to_vector() - position.to_vector()).abs();
1149            let area = multi_click_cfg.area;
1150            dist.x <= area.width && dist.y <= area.height
1151        };
1152
1153        let click_trigger = if entry.click_count == 0 || !double_allowed {
1154            entry.click_count = 0;
1155            click_mode.single
1156        } else {
1157            click_mode.double
1158        };
1159
1160        let click = match state {
1161            ButtonState::Pressed => {
1162                entry.pressed = true;
1163                entry.press_stop_handle = stop_handle.clone();
1164                matches!(click_trigger, ClickTrigger::Press)
1165            }
1166            ButtonState::Released => {
1167                entry.repeat_count = 0;
1168                entry.repeat_timer = None;
1169                if mem::take(&mut entry.pressed) && !entry.press_stop_handle.is_stopped() {
1170                    matches!(click_trigger, ClickTrigger::PressRelease | ClickTrigger::Release)
1171                } else {
1172                    matches!(click_trigger, ClickTrigger::Release)
1173                }
1174            }
1175        };
1176
1177        if click_mode.repeat {
1178            if click {
1179                let t = self.repeat_config.get().start_delay;
1180                let t = TIMERS.deadline(t);
1181                t.hook(|t| {
1182                    let elapsed = t.value().has_elapsed();
1183                    if elapsed {
1184                        MOUSE_SV.write().on_repeat_timer_elapsed();
1185                    }
1186                    !elapsed
1187                })
1188                .perm();
1189                entry.repeat_timer = Some(t);
1190                entry.repeat_count = 0;
1191            }
1192        } else {
1193            entry.repeat_timer = None;
1194            entry.repeat_count = 0;
1195        }
1196
1197        let capture_info = POINTER_CAPTURE.current_capture().get();
1198
1199        let now = INSTANT.now();
1200        let args = MouseInputArgs::new(
1201            now,
1202            stop_handle.clone(),
1203            window_id,
1204            device_id,
1205            button,
1206            position,
1207            self.modifiers,
1208            state,
1209            hits.clone(),
1210            wgt_path.clone(),
1211            capture_info,
1212            click,
1213        );
1214
1215        // on_mouse_input
1216        MOUSE_INPUT_EVENT.notify(args);
1217
1218        if click {
1219            if double_allowed {
1220                entry.click_count += 1;
1221            } else {
1222                entry.click_count = 1;
1223            }
1224
1225            entry.last_click = now;
1226            entry.last_pos = position;
1227
1228            let args = MouseClickArgs::new(
1229                now,
1230                stop_handle,
1231                window_id,
1232                device_id,
1233                button,
1234                position,
1235                self.modifiers,
1236                NonZeroU32::new(entry.click_count).unwrap(),
1237                false,
1238                hits,
1239                wgt_path,
1240            );
1241
1242            // on_mouse_click
1243            MOUSE_CLICK_EVENT.notify(args);
1244        }
1245    }
1246
1247    fn on_cursor_moved(&mut self, window_id: WindowId, device_id: InputDeviceId, coalesced_pos: Vec<DipPoint>, mut position: DipPoint) {
1248        let mut moved = Some(window_id) != self.pos_window || Some(device_id) != self.pos_device;
1249
1250        if moved {
1251            // if is over another window now.
1252            self.pos_window = Some(window_id);
1253            self.pos_device = Some(device_id);
1254        }
1255
1256        moved |= position != self.pos;
1257
1258        if moved {
1259            // if moved to another window or within the same window.
1260
1261            self.pos = position;
1262
1263            // mouse_move data
1264            let mut frame_info = match WINDOWS.widget_tree(window_id) {
1265                Some(f) => f,
1266                None => {
1267                    // window not found
1268                    if let Some(hovered) = self.hovered_value.take() {
1269                        let capture = POINTER_CAPTURE.current_capture().get();
1270                        let args = MouseHoverArgs::now(
1271                            window_id,
1272                            device_id,
1273                            position,
1274                            HitTestInfo::no_hits(window_id),
1275                            Some(hovered),
1276                            None,
1277                            capture.clone(),
1278                            capture,
1279                        );
1280                        MOUSE_HOVERED_EVENT.notify(args);
1281                    }
1282                    return;
1283                }
1284            };
1285
1286            let mut pos_hits = frame_info.root().hit_test(position.to_px(frame_info.scale_factor()));
1287
1288            let target = if let Some(t) = pos_hits.target() {
1289                if let Some(w) = frame_info.get(t.widget_id) {
1290                    if let Some(f) = w.nested_window_tree() {
1291                        // nested window hit
1292                        frame_info = f;
1293                        let factor = frame_info.scale_factor();
1294                        let pos = position.to_px(factor);
1295                        let pos = w.inner_transform().inverse().and_then(|t| t.transform_point(pos)).unwrap_or(pos);
1296                        pos_hits = frame_info.root().hit_test(pos);
1297                        position = pos.to_dip(factor);
1298                        pos_hits
1299                            .target()
1300                            .and_then(|h| frame_info.get(h.widget_id))
1301                            .map(|w| w.interaction_path())
1302                            .unwrap_or_else(|| frame_info.root().interaction_path())
1303                    } else {
1304                        w.interaction_path()
1305                    }
1306                } else {
1307                    tracing::error!("hits target `{}` not found", t.widget_id);
1308                    frame_info.root().interaction_path()
1309                }
1310            } else {
1311                frame_info.root().interaction_path()
1312            }
1313            .unblocked();
1314
1315            self.position.set(Some(MousePosition {
1316                window_id: frame_info.window_id(),
1317                position,
1318                timestamp: INSTANT.now(),
1319            }));
1320
1321            self.hits = Some(pos_hits.clone());
1322
1323            let capture = POINTER_CAPTURE.current_capture().get();
1324
1325            // mouse_enter/mouse_leave.
1326            let hovered_args = if self.hovered_value != target {
1327                self.hovered.set(target.clone());
1328                let prev_target = mem::replace(&mut self.hovered_value, target.clone());
1329                let args = MouseHoverArgs::now(
1330                    frame_info.window_id(),
1331                    device_id,
1332                    position,
1333                    pos_hits.clone(),
1334                    prev_target,
1335                    target.clone(),
1336                    capture.clone(),
1337                    capture.clone(),
1338                );
1339                Some(args)
1340            } else {
1341                None
1342            };
1343
1344            // mouse_move
1345            if let Some(target) = target {
1346                let args = MouseMoveArgs::now(
1347                    frame_info.window_id(),
1348                    device_id,
1349                    self.modifiers,
1350                    coalesced_pos,
1351                    position,
1352                    pos_hits,
1353                    target,
1354                    capture,
1355                );
1356                MOUSE_MOVE_EVENT.notify(args);
1357            }
1358
1359            if let Some(args) = hovered_args {
1360                MOUSE_HOVERED_EVENT.notify(args);
1361            }
1362        } else if coalesced_pos.is_empty() {
1363            tracing::debug!("RawCursorMoved did not actually move")
1364        }
1365    }
1366
1367    fn on_scroll(&self, window_id: WindowId, device_id: InputDeviceId, delta: MouseScrollDelta, phase: TouchPhase) {
1368        let position = if self.pos_window == Some(window_id) {
1369            self.pos
1370        } else {
1371            DipPoint::default()
1372        };
1373
1374        let hits = self.hits.clone().unwrap_or_else(|| HitTestInfo::no_hits(window_id));
1375
1376        let frame_info = WINDOWS.widget_tree(hits.window_id()).unwrap();
1377
1378        let target = hits
1379            .target()
1380            .and_then(|t| frame_info.get(t.widget_id).map(|w| w.interaction_path()))
1381            .unwrap_or_else(|| frame_info.root().interaction_path());
1382
1383        if let Some(target) = target.unblocked() {
1384            let args = MouseWheelArgs::now(hits.window_id(), device_id, position, self.modifiers, delta, phase, hits, target);
1385            MOUSE_WHEEL_EVENT.notify(args);
1386        }
1387    }
1388
1389    fn on_cursor_left_window(&mut self, window_id: WindowId, device_id: InputDeviceId) {
1390        if Some(window_id) == self.pos_window.take() {
1391            self.position.set(None);
1392            if let Some(path) = self.hovered_value.take() {
1393                self.hovered.set(None);
1394                let capture = POINTER_CAPTURE.current_capture().get();
1395                let args = MouseHoverArgs::now(
1396                    window_id,
1397                    device_id,
1398                    self.pos,
1399                    HitTestInfo::no_hits(window_id),
1400                    Some(path),
1401                    None,
1402                    capture.clone(),
1403                    capture,
1404                );
1405                MOUSE_HOVERED_EVENT.notify(args);
1406            }
1407        }
1408    }
1409
1410    fn on_window_blur(&mut self, prev_window: WindowId, new_window: Option<WindowId>) {
1411        if new_window.is_some() {
1412            if let Some(p) = self.pos_window {
1413                // last hovered window losing focus, and is not just focusing a nested window
1414                if p == prev_window && (new_window.is_none() || new_window != self.hits.as_ref().map(|h| h.window_id())) {
1415                    self.clean_all_state();
1416                }
1417            }
1418        } else {
1419            self.clean_all_state();
1420        }
1421    }
1422
1423    /// Call after a frame or info rebuild.
1424    fn continue_hovered(&mut self, mut window_id: WindowId) {
1425        if self.pos_window == Some(window_id) {
1426            // update hovered if widgets moved under the cursor position.
1427            let mut frame_info = match WINDOWS.widget_tree(window_id) {
1428                Some(f) => f,
1429                None => {
1430                    self.clean_all_state();
1431                    return;
1432                }
1433            };
1434            let mut pos_hits = frame_info.root().hit_test(self.pos.to_px(frame_info.scale_factor()));
1435            let mut position = self.pos;
1436            let target = if let Some(t) = pos_hits.target() {
1437                if let Some(w) = frame_info.get(t.widget_id) {
1438                    if let Some(f) = w.nested_window_tree() {
1439                        frame_info = f;
1440                        let factor = frame_info.scale_factor();
1441                        let pos = self.pos.to_px(factor);
1442                        let pos = w.inner_transform().inverse().and_then(|t| t.transform_point(pos)).unwrap_or(pos);
1443                        pos_hits = frame_info.root().hit_test(pos);
1444                        window_id = frame_info.window_id();
1445                        position = pos.to_dip(factor);
1446                        pos_hits
1447                            .target()
1448                            .and_then(|h| frame_info.get(h.widget_id))
1449                            .map(|w| w.interaction_path())
1450                            .unwrap_or_else(|| frame_info.root().interaction_path())
1451                    } else {
1452                        w.interaction_path()
1453                    }
1454                } else {
1455                    tracing::error!("hits target `{}` not found", t.widget_id);
1456                    frame_info.root().interaction_path()
1457                }
1458            } else {
1459                frame_info.root().interaction_path()
1460            }
1461            .unblocked();
1462            self.hits = Some(pos_hits.clone());
1463
1464            if self.hovered_value != target {
1465                let capture = POINTER_CAPTURE.current_capture().get();
1466                let prev = mem::replace(&mut self.hovered_value, target.clone());
1467                let args = MouseHoverArgs::now(window_id, None, position, pos_hits, prev, target, capture.clone(), capture);
1468                MOUSE_HOVERED_EVENT.notify(args);
1469            }
1470        }
1471    }
1472
1473    fn clean_all_state(&mut self) {
1474        if self.pos_window.take().is_some()
1475            && let Some(path) = self.hovered_value.take()
1476        {
1477            let window_id = path.window_id();
1478            self.buttons.with(|b| {
1479                for btn in b {
1480                    let args = MouseInputArgs::now(
1481                        window_id,
1482                        None,
1483                        *btn,
1484                        DipPoint::new(Dip::new(-1), Dip::new(-1)),
1485                        ModifiersState::empty(),
1486                        ButtonState::Released,
1487                        HitTestInfo::no_hits(window_id),
1488                        path.clone(),
1489                        None,
1490                        false,
1491                    );
1492                    MOUSE_INPUT_EVENT.notify(args);
1493                }
1494            });
1495
1496            let args = MouseHoverArgs::now(
1497                window_id,
1498                None,
1499                DipPoint::new(Dip::new(-1), Dip::new(-1)),
1500                HitTestInfo::no_hits(window_id),
1501                Some(path),
1502                None,
1503                None,
1504                None,
1505            );
1506            MOUSE_HOVERED_EVENT.notify(args);
1507        }
1508        self.buttons.set(vec![]);
1509        self.clicking.clear();
1510        self.pos_device = None;
1511        self.pos_window = None;
1512        self.hits = None;
1513        self.position.set(None);
1514        self.hovered.set(None);
1515    }
1516
1517    fn on_pointer_capture(&mut self, prev_capture: &Option<CaptureInfo>, new_capture: &Option<CaptureInfo>) {
1518        if let Some(path) = &self.hovered_value
1519            && self.pos_window.is_some()
1520        {
1521            let window_id = path.window_id();
1522            let hover_args = MouseHoverArgs::now(
1523                window_id,
1524                self.pos_device.unwrap(),
1525                self.pos,
1526                self.hits.clone().unwrap_or_else(|| HitTestInfo::no_hits(window_id)),
1527                Some(path.clone()),
1528                Some(path.clone()),
1529                prev_capture.clone(),
1530                new_capture.clone(),
1531            );
1532            MOUSE_HOVERED_EVENT.notify(hover_args);
1533        }
1534    }
1535}
1536impl MouseService {
1537    fn on_repeat_timer_elapsed(&mut self) {
1538        // update pressed repeat clicks
1539        for (btn, info) in self.clicking.iter_mut() {
1540            if let Some(timer) = info.repeat_timer.take() {
1541                // is repeat mode
1542                if timer.with_new(|t| t.has_elapsed()).unwrap_or(false) {
1543                    // time to repeat
1544                    info.repeat_count = info.repeat_count.saturating_add(1);
1545
1546                    if let Some(dv) = self.pos_device
1547                        && let Some(tree) = WINDOWS.widget_tree(info.path.window_id())
1548                    {
1549                        // probably still valid
1550
1551                        let hit_test = tree.root().hit_test(self.pos.to_px(tree.scale_factor()));
1552
1553                        // get the hit target, constrained by capture
1554                        let mut target = None;
1555                        if let Some(hit) = hit_test.target().map(|t| tree.get(t.widget_id).unwrap()) {
1556                            target = hit.path().shared_ancestor(info.path.as_path()).map(|c| c.into_owned());
1557                        }
1558                        if let Some(c) = POINTER_CAPTURE.current_capture().get() {
1559                            match c.mode {
1560                                CaptureMode::Window => {
1561                                    if let Some(t) = &target {
1562                                        if t.window_id() != c.target.window_id() {
1563                                            target = None; // captured in other window, cancel repeat
1564                                        }
1565                                    } else {
1566                                        // no hit, but window capture
1567                                        target = Some(tree.root().path());
1568                                    }
1569                                }
1570                                CaptureMode::Subtree => {
1571                                    if let Some(t) = &target {
1572                                        target = c.target.shared_ancestor(t).map(|c| c.into_owned());
1573                                    } else {
1574                                        target = Some(c.target);
1575                                    }
1576                                }
1577                                CaptureMode::Widget => {
1578                                    target = Some(c.target);
1579                                }
1580                            }
1581                        }
1582
1583                        if let Some(target) = target {
1584                            // if still has a target
1585                            if let Some(target) = tree.get(target.widget_id()).and_then(|w| w.interaction_path().unblocked()) {
1586                                // and it is unblocked
1587
1588                                // notify repeat
1589                                let args = MouseClickArgs::now(
1590                                    target.window_id(),
1591                                    dv,
1592                                    *btn,
1593                                    self.pos,
1594                                    self.modifiers,
1595                                    NonZeroU32::new(info.repeat_count).unwrap(),
1596                                    true,
1597                                    hit_test,
1598                                    target,
1599                                );
1600                                MOUSE_CLICK_EVENT.notify(args);
1601
1602                                // continue timer
1603                                let t = MOUSE.repeat_config().get().interval;
1604                                let t = TIMERS.deadline(t);
1605                                t.hook(|t| {
1606                                    let elapsed = t.value().has_elapsed();
1607                                    if elapsed {
1608                                        MOUSE_SV.write().on_repeat_timer_elapsed();
1609                                    }
1610                                    !elapsed
1611                                })
1612                                .perm();
1613                                info.repeat_timer = Some(t);
1614                            }
1615                        }
1616                    }
1617                } else {
1618                    // not time to repeat
1619                    info.repeat_timer = Some(timer);
1620                }
1621            }
1622        }
1623    }
1624}