zng_ext_input/
mouse.rs

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