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    APP, 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::InputDeviceId,
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::{IntoVar, Var, context_var, impl_from_and_into_var, 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: InputDeviceId,
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<InputDeviceId>,
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: InputDeviceId,
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<InputDeviceId>,
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: InputDeviceId,
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            && !cap.allows()
356        {
357            return false;
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            && !cap.allows()
375        {
376            return false;
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        match &self.prev_target {
391            Some(t) => t.contains_enabled(widget_id),
392            None => false,
393        }
394    }
395
396    /// Returns `true` if the widget was disabled in [`prev_target`].
397    ///
398    /// [`prev_target`]: Self::prev_target
399    pub fn was_disabled(&self, widget_id: WidgetId) -> bool {
400        match &self.prev_target {
401            Some(t) => t.contains_disabled(widget_id),
402            None => false,
403        }
404    }
405
406    /// Returns `true` if the widget is enabled in [`target`].
407    ///
408    /// [`target`]: Self::target
409    pub fn is_enabled(&self, widget_id: WidgetId) -> bool {
410        match &self.target {
411            Some(t) => t.contains_enabled(widget_id),
412            None => false,
413        }
414    }
415
416    /// Returns `true` if the widget is disabled in [`target`].
417    ///
418    /// [`target`]: Self::target
419    pub fn is_disabled(&self, widget_id: WidgetId) -> bool {
420        match &self.target {
421            Some(t) => t.contains_disabled(widget_id),
422            None => false,
423        }
424    }
425
426    /// Gets position in the widget inner bounds.
427    pub fn position_wgt(&self) -> Option<PxPoint> {
428        WIDGET.win_point_to_wgt(self.position)
429    }
430}
431
432impl MouseMoveArgs {
433    /// If [`capture`] is `None` or [`allows`] the [`WIDGET`] to receive this event.
434    ///
435    /// [`capture`]: Self::capture
436    /// [`allows`]: CaptureInfo::allows
437    /// [`WIDGET`]: zng_app::widget::WIDGET
438    pub fn capture_allows(&self) -> bool {
439        self.capture.as_ref().map(|c| c.allows()).unwrap_or(true)
440    }
441
442    /// Gets position in the widget inner bounds.
443    pub fn position_wgt(&self) -> Option<PxPoint> {
444        WIDGET.win_point_to_wgt(self.position)
445    }
446}
447
448impl MouseInputArgs {
449    /// If [`capture`] is `None` or [`allows`] the [`WIDGET`] to receive this event.
450    ///
451    /// [`capture`]: Self::capture
452    /// [`allows`]: CaptureInfo::allows
453    /// [`WIDGET`]: zng_app::widget::WIDGET
454    pub fn capture_allows(&self) -> bool {
455        self.capture.as_ref().map(|c| c.allows()).unwrap_or(true)
456    }
457
458    /// If the `widget_id` is in the [`target`].
459    ///
460    /// [`target`]: Self::target
461    pub fn is_over(&self, widget_id: WidgetId) -> bool {
462        self.target.contains(widget_id)
463    }
464
465    /// If the [`button`] is the primary.
466    ///
467    /// [`button`]: Self::button
468    pub fn is_primary(&self) -> bool {
469        self.button == MouseButton::Left
470    }
471
472    /// If the [`button`](Self::button) is the context (right).
473    pub fn is_context(&self) -> bool {
474        self.button == MouseButton::Right
475    }
476
477    /// If the [`state`] is pressed.
478    ///
479    /// [`state`]: Self::state
480    pub fn is_mouse_down(&self) -> bool {
481        self.state == ButtonState::Pressed
482    }
483
484    /// If the [`state`] is released.
485    ///
486    /// [`state`]: Self::state
487    pub fn is_mouse_up(&self) -> bool {
488        self.state == ButtonState::Released
489    }
490
491    /// Gets position in the widget inner bounds.
492    pub fn position_wgt(&self) -> Option<PxPoint> {
493        WIDGET.win_point_to_wgt(self.position)
494    }
495}
496
497impl MouseClickArgs {
498    /// If the [`button`](Self::button) is the primary.
499    pub fn is_primary(&self) -> bool {
500        self.button == MouseButton::Left
501    }
502
503    /// If the [`button`](Self::button) is the context (right).
504    pub fn is_context(&self) -> bool {
505        self.button == MouseButton::Right
506    }
507
508    /// If the [`click_count`](Self::click_count) is `1`.
509    pub fn is_single(&self) -> bool {
510        self.click_count.get() == 1
511    }
512
513    /// If the [`click_count`](Self::click_count) is `2`.
514    pub fn is_double(&self) -> bool {
515        self.click_count.get() == 2
516    }
517
518    /// If the [`click_count`](Self::click_count) is `3`.
519    pub fn is_triple(&self) -> bool {
520        self.click_count.get() == 3
521    }
522
523    /// Gets position in the widget inner bounds.
524    pub fn position_wgt(&self) -> Option<PxPoint> {
525        WIDGET.win_point_to_wgt(self.position)
526    }
527}
528
529impl MouseWheelArgs {
530    /// Swaps the delta axis if [`modifiers`] contains `SHIFT`.
531    ///
532    /// [`modifiers`]: Self::modifiers
533    pub fn shifted_delta(&self) -> MouseScrollDelta {
534        if self.modifiers.has_shift() {
535            match self.delta {
536                MouseScrollDelta::LineDelta(x, y) => MouseScrollDelta::LineDelta(y, x),
537                MouseScrollDelta::PixelDelta(x, y) => MouseScrollDelta::PixelDelta(y, x),
538                _ => unimplemented!(),
539            }
540        } else {
541            self.delta
542        }
543    }
544
545    /// If the modifiers allow the event to be used for scrolling.
546    ///
547    /// Is `true` if only `SHIFT`, `ALT` or none modifiers are pressed. If `true` the
548    /// [`scroll_delta`] method returns a value.
549    ///
550    /// If contextual [`CTRL_SCROLL_VAR`] is `true` the `CTRL` modifier must be pressed for this to be `true`.
551    ///
552    /// [`scroll_delta`]: Self::scroll_delta
553    pub fn is_scroll(&self) -> bool {
554        if CTRL_SCROLL_VAR.get() {
555            self.modifiers
556                .is_only(ModifiersState::CTRL | ModifiersState::SHIFT | ModifiersState::ALT)
557        } else {
558            self.modifiers.is_empty() || self.modifiers.is_only(ModifiersState::SHIFT | ModifiersState::ALT)
559        }
560    }
561
562    /// Returns the delta for a scrolling operation, depending on the [`modifiers`].
563    ///
564    /// If [`is_scroll`] scales the delta by `alt_factor`, then, if no more modifiers are pressed returns
565    /// the scaled delta, if only `SHIFT` is pressed returns the swapped delta, otherwise returns `None`.
566    ///
567    /// [`is_scroll`]: Self::is_scroll
568    /// [`modifiers`]: Self::modifiers
569    pub fn scroll_delta(&self, alt_factor: impl Into<Factor>) -> Option<MouseScrollDelta> {
570        let mut modifiers = self.modifiers;
571        if CTRL_SCROLL_VAR.get() && !modifiers.take_ctrl() {
572            return None;
573        }
574        let mut delta = self.delta;
575        if modifiers.take_alt() {
576            let alt_factor = alt_factor.into();
577            delta = match delta {
578                MouseScrollDelta::LineDelta(x, y) => MouseScrollDelta::LineDelta(x * alt_factor.0, y * alt_factor.0),
579                MouseScrollDelta::PixelDelta(x, y) => MouseScrollDelta::PixelDelta(x * alt_factor.0, y * alt_factor.0),
580                _ => return None,
581            };
582        }
583
584        if modifiers.is_empty() {
585            Some(delta)
586        } else if modifiers.is_only_shift() {
587            Some(match delta {
588                MouseScrollDelta::LineDelta(x, y) => MouseScrollDelta::LineDelta(y, x),
589                MouseScrollDelta::PixelDelta(x, y) => MouseScrollDelta::PixelDelta(y, x),
590                _ => return None,
591            })
592        } else {
593            None
594        }
595    }
596
597    /// If the modifiers allow the event to be used for zooming.
598    ///
599    /// Is `true` if only `CTRL` is pressed. If `true` the [`zoom_delta`] method returns a value.
600    ///
601    /// If contextual [`CTRL_SCROLL_VAR`] is `true` no modifiers must be pressed for this to be `true`.
602    ///
603    /// [`zoom_delta`]: Self::zoom_delta
604    pub fn is_zoom(&self) -> bool {
605        if CTRL_SCROLL_VAR.get() {
606            self.modifiers.is_empty()
607        } else {
608            self.modifiers.is_only_ctrl()
609        }
610    }
611
612    /// Returns the delta for a zoom-in/out operation, depending on the [`modifiers`].
613    ///
614    /// If [`is_zoom`] returns the scroll delta, otherwise returns `None`.
615    ///
616    /// [`is_zoom`]: Self::is_zoom
617    /// [`modifiers`]: Self::modifiers
618    pub fn zoom_delta(&self) -> Option<MouseScrollDelta> {
619        if self.is_zoom() { Some(self.delta) } else { None }
620    }
621
622    /// Gets position in the widget inner bounds.
623    pub fn position_wgt(&self) -> Option<PxPoint> {
624        WIDGET.win_point_to_wgt(self.position)
625    }
626}
627
628context_var! {
629    /// Defines if [`MouseWheelArgs`] gesture is [`is_scroll`] when `CTRL` is pressed and [`is_zoom`] when no modifier is pressed.
630    ///
631    /// Is `false` by default.
632    ///
633    /// [`is_scroll`]: MouseWheelArgs::is_scroll
634    /// [`is_zoom`]: MouseWheelArgs::is_zoom
635    pub static CTRL_SCROLL_VAR: bool = false;
636}
637
638event! {
639    /// Mouse move event.
640    pub static MOUSE_MOVE_EVENT: MouseMoveArgs;
641
642    /// Mouse down or up event.
643    pub static MOUSE_INPUT_EVENT: MouseInputArgs;
644
645    /// Mouse click event, any [`click_count`](MouseClickArgs::click_count).
646    pub static MOUSE_CLICK_EVENT: MouseClickArgs;
647
648    /// The top-most hovered widget changed or pointer capture changed.
649    pub static MOUSE_HOVERED_EVENT: MouseHoverArgs;
650
651    /// Mouse wheel scroll event.
652    pub static MOUSE_WHEEL_EVENT: MouseWheelArgs;
653}
654
655struct ClickingInfo {
656    path: InteractionPath,
657    press_stop_handle: EventPropagationHandle,
658
659    pressed: bool,
660    last_pos: DipPoint,
661    last_click: DInstant,
662    click_count: u32,
663
664    repeat_timer: Option<DeadlineVar>,
665    repeat_count: u32,
666}
667
668/// Application extension that provides mouse events and service.
669///
670/// # Events
671///
672/// Events this extension provides.
673///
674/// * [`MOUSE_MOVE_EVENT`]
675/// * [`MOUSE_INPUT_EVENT`]
676/// * [`MOUSE_CLICK_EVENT`]
677/// * [`MOUSE_HOVERED_EVENT`]
678///
679/// # Services
680///
681/// Services this extension provides.
682///
683/// * [`MOUSE`]
684pub struct MouseManager {
685    // last cursor move position (scaled).
686    pos: DipPoint,
687    // last cursor move over `pos_window` and source device.
688    pos_window: Option<WindowId>,
689    pos_device: Option<InputDeviceId>,
690    // last cursor move hit-test (on the pos_window or a nested window).
691    hits: Option<HitTestInfo>,
692
693    /// last modifiers.
694    modifiers: ModifiersState,
695
696    hovered: Option<InteractionPath>,
697    clicking: HashMap<MouseButton, ClickingInfo>,
698}
699impl Default for MouseManager {
700    fn default() -> Self {
701        MouseManager {
702            pos: DipPoint::zero(),
703            pos_window: None,
704            pos_device: None,
705            hits: None,
706
707            modifiers: ModifiersState::default(),
708
709            hovered: None,
710            clicking: HashMap::default(),
711        }
712    }
713}
714impl MouseManager {
715    fn on_mouse_input(&mut self, mut window_id: WindowId, device_id: InputDeviceId, state: ButtonState, button: MouseButton) {
716        let mouse = MOUSE_SV.read();
717
718        let mut position = if self.pos_window == Some(window_id) {
719            self.pos
720        } else {
721            DipPoint::default()
722        };
723
724        let hits = self.hits.clone().unwrap_or_else(|| HitTestInfo::no_hits(window_id));
725
726        let wgt_tree = match WINDOWS.widget_tree(hits.window_id()) {
727            Ok(t) => t,
728            Err(e) => {
729                tracing::error!("cannot find clicked window, {e:?}");
730                return;
731            }
732        };
733
734        if hits.window_id() != window_id {
735            // is over nested window
736            window_id = hits.window_id();
737            position = hits.point().to_dip(wgt_tree.scale_factor());
738        }
739
740        let (wgt_path, click_mode) = hits
741            .target()
742            .and_then(|t| wgt_tree.get(t.widget_id).map(|w| (w.interaction_path(), w.click_mode())))
743            .unwrap_or_else(|| (wgt_tree.root().interaction_path(), wgt_tree.root().click_mode()));
744
745        let wgt_path = match wgt_path.unblocked() {
746            Some(p) => p,
747            None => return, // entire window blocked
748        };
749
750        match state {
751            ButtonState::Pressed => {
752                if !mouse.buttons.with(|b| b.contains(&button)) {
753                    mouse.buttons.modify(move |btns| btns.push(button));
754                }
755            }
756            ButtonState::Released => {
757                if mouse.buttons.with(|b| b.contains(&button)) {
758                    mouse.buttons.modify(move |btns| {
759                        if let Some(i) = btns.iter().position(|k| *k == button) {
760                            btns.swap_remove(i);
761                        }
762                    });
763                }
764            }
765        }
766
767        let stop_handle = EventPropagationHandle::new();
768
769        let entry = self.clicking.entry(button).or_insert_with(|| ClickingInfo {
770            path: wgt_path.clone(),
771            press_stop_handle: stop_handle.clone(),
772            last_click: DInstant::EPOCH,
773            last_pos: position,
774            pressed: false,
775            click_count: 0,
776            repeat_timer: None,
777            repeat_count: 0,
778        });
779
780        if entry.path != wgt_path {
781            let actual_change = entry.path.as_path() != wgt_path.as_path();
782            // else only interactivity change mid-click
783
784            entry.path = wgt_path.clone();
785            if actual_change {
786                entry.press_stop_handle = stop_handle.clone();
787                entry.pressed = false;
788                entry.click_count = 0;
789                entry.repeat_timer = None;
790                entry.repeat_count = 0;
791            }
792        }
793
794        let multi_click_cfg = mouse.multi_click_config.get();
795
796        let double_allowed = entry.last_click.elapsed() <= multi_click_cfg.time && {
797            let dist = (entry.last_pos.to_vector() - position.to_vector()).abs();
798            let area = multi_click_cfg.area;
799            dist.x <= area.width && dist.y <= area.height
800        };
801
802        let click_gesture = if entry.click_count == 0 || !double_allowed {
803            entry.click_count = 0;
804            click_mode.single
805        } else {
806            click_mode.double
807        };
808
809        let click = match state {
810            ButtonState::Pressed => {
811                entry.pressed = true;
812                entry.press_stop_handle = stop_handle.clone();
813                matches!(click_gesture, ClickTrigger::Press)
814            }
815            ButtonState::Released => {
816                entry.repeat_count = 0;
817                entry.repeat_timer = None;
818                if mem::take(&mut entry.pressed) && !entry.press_stop_handle.is_stopped() {
819                    matches!(click_gesture, ClickTrigger::PressRelease | ClickTrigger::Release)
820                } else {
821                    matches!(click_gesture, ClickTrigger::Release)
822                }
823            }
824        };
825
826        if click_mode.repeat {
827            if click {
828                let t = mouse.repeat_config.get().start_delay;
829                entry.repeat_timer = Some(TIMERS.deadline(t));
830                entry.repeat_count = 0;
831            }
832        } else {
833            entry.repeat_timer = None;
834            entry.repeat_count = 0;
835        }
836
837        let capture_info = POINTER_CAPTURE.current_capture_value();
838
839        let now = INSTANT.now();
840        let args = MouseInputArgs::new(
841            now,
842            stop_handle.clone(),
843            window_id,
844            device_id,
845            button,
846            position,
847            self.modifiers,
848            state,
849            hits.clone(),
850            wgt_path.clone(),
851            capture_info,
852            click,
853        );
854
855        // on_mouse_input
856        MOUSE_INPUT_EVENT.notify(args);
857
858        if click {
859            if double_allowed {
860                entry.click_count += 1;
861            } else {
862                entry.click_count = 1;
863            }
864
865            entry.last_click = now;
866            entry.last_pos = position;
867
868            let args = MouseClickArgs::new(
869                now,
870                stop_handle,
871                window_id,
872                device_id,
873                button,
874                position,
875                self.modifiers,
876                NonZeroU32::new(entry.click_count).unwrap(),
877                false,
878                hits,
879                wgt_path,
880            );
881
882            // on_mouse_click
883            MOUSE_CLICK_EVENT.notify(args);
884        }
885    }
886
887    fn on_cursor_moved(&mut self, window_id: WindowId, device_id: InputDeviceId, coalesced_pos: Vec<DipPoint>, mut position: DipPoint) {
888        let mut moved = Some(window_id) != self.pos_window || Some(device_id) != self.pos_device;
889
890        if moved {
891            // if is over another window now.
892            self.pos_window = Some(window_id);
893            self.pos_device = Some(device_id);
894        }
895
896        moved |= position != self.pos;
897
898        if moved {
899            // if moved to another window or within the same window.
900
901            self.pos = position;
902
903            // mouse_move data
904            let mut frame_info = match WINDOWS.widget_tree(window_id) {
905                Ok(f) => f,
906                Err(_) => {
907                    // window not found
908                    if let Some(hovered) = self.hovered.take() {
909                        let capture = POINTER_CAPTURE.current_capture_value();
910                        let args = MouseHoverArgs::now(
911                            window_id,
912                            device_id,
913                            position,
914                            HitTestInfo::no_hits(window_id),
915                            Some(hovered),
916                            None,
917                            capture.clone(),
918                            capture,
919                        );
920                        MOUSE_HOVERED_EVENT.notify(args);
921                    }
922                    return;
923                }
924            };
925
926            let mut pos_hits = frame_info.root().hit_test(position.to_px(frame_info.scale_factor()));
927
928            let target = if let Some(t) = pos_hits.target() {
929                if let Some(w) = frame_info.get(t.widget_id) {
930                    if let Some(f) = w.nested_window_tree() {
931                        // nested window hit
932                        frame_info = f;
933                        let factor = frame_info.scale_factor();
934                        let pos = position.to_px(factor);
935                        let pos = w.inner_transform().inverse().and_then(|t| t.transform_point(pos)).unwrap_or(pos);
936                        pos_hits = frame_info.root().hit_test(pos);
937                        position = pos.to_dip(factor);
938                        pos_hits
939                            .target()
940                            .and_then(|h| frame_info.get(h.widget_id))
941                            .map(|w| w.interaction_path())
942                            .unwrap_or_else(|| frame_info.root().interaction_path())
943                    } else {
944                        w.interaction_path()
945                    }
946                } else {
947                    tracing::error!("hits target `{}` not found", t.widget_id);
948                    frame_info.root().interaction_path()
949                }
950            } else {
951                frame_info.root().interaction_path()
952            }
953            .unblocked();
954
955            MOUSE_SV.read().position.set(Some(MousePosition {
956                window_id: frame_info.window_id(),
957                position,
958                timestamp: INSTANT.now(),
959            }));
960
961            self.hits = Some(pos_hits.clone());
962
963            let capture = POINTER_CAPTURE.current_capture_value();
964
965            // mouse_enter/mouse_leave.
966            let hovered_args = if self.hovered != target {
967                MOUSE_SV.read().hovered.set(target.clone());
968                let prev_target = mem::replace(&mut self.hovered, target.clone());
969                let args = MouseHoverArgs::now(
970                    frame_info.window_id(),
971                    device_id,
972                    position,
973                    pos_hits.clone(),
974                    prev_target,
975                    target.clone(),
976                    capture.clone(),
977                    capture.clone(),
978                );
979                Some(args)
980            } else {
981                None
982            };
983
984            // mouse_move
985            if let Some(target) = target {
986                let args = MouseMoveArgs::now(
987                    frame_info.window_id(),
988                    device_id,
989                    self.modifiers,
990                    coalesced_pos,
991                    position,
992                    pos_hits,
993                    target,
994                    capture,
995                );
996                MOUSE_MOVE_EVENT.notify(args);
997            }
998
999            if let Some(args) = hovered_args {
1000                MOUSE_HOVERED_EVENT.notify(args);
1001            }
1002        } else if coalesced_pos.is_empty() {
1003            tracing::debug!("RawCursorMoved did not actually move")
1004        }
1005    }
1006
1007    fn on_scroll(&self, window_id: WindowId, device_id: InputDeviceId, delta: MouseScrollDelta, phase: TouchPhase) {
1008        let position = if self.pos_window == Some(window_id) {
1009            self.pos
1010        } else {
1011            DipPoint::default()
1012        };
1013
1014        let hits = self.hits.clone().unwrap_or_else(|| HitTestInfo::no_hits(window_id));
1015
1016        let frame_info = WINDOWS.widget_tree(hits.window_id()).unwrap();
1017
1018        let target = hits
1019            .target()
1020            .and_then(|t| frame_info.get(t.widget_id).map(|w| w.interaction_path()))
1021            .unwrap_or_else(|| frame_info.root().interaction_path());
1022
1023        if let Some(target) = target.unblocked() {
1024            let args = MouseWheelArgs::now(hits.window_id(), device_id, position, self.modifiers, delta, phase, hits, target);
1025            MOUSE_WHEEL_EVENT.notify(args);
1026        }
1027    }
1028
1029    fn on_cursor_left_window(&mut self, window_id: WindowId, device_id: InputDeviceId) {
1030        if Some(window_id) == self.pos_window.take() {
1031            MOUSE_SV.read().position.set(None);
1032            if let Some(path) = self.hovered.take() {
1033                MOUSE_SV.read().hovered.set(None);
1034                let capture = POINTER_CAPTURE.current_capture_value();
1035                let args = MouseHoverArgs::now(
1036                    window_id,
1037                    device_id,
1038                    self.pos,
1039                    HitTestInfo::no_hits(window_id),
1040                    Some(path),
1041                    None,
1042                    capture.clone(),
1043                    capture,
1044                );
1045                MOUSE_HOVERED_EVENT.notify(args);
1046            }
1047        }
1048    }
1049
1050    fn on_window_blur(&mut self, prev_window: WindowId, new_window: Option<WindowId>) {
1051        if new_window.is_some() {
1052            if let Some(p) = self.pos_window {
1053                // last hovered window losing focus, and is not just focusing a nested window
1054                if p == prev_window && (new_window.is_none() || new_window != self.hits.as_ref().map(|h| h.window_id())) {
1055                    self.clean_all_state();
1056                }
1057            }
1058        } else {
1059            self.clean_all_state();
1060        }
1061    }
1062
1063    /// Call after a frame or info rebuild.
1064    fn continue_hovered(&mut self, mut window_id: WindowId) {
1065        if self.pos_window == Some(window_id) {
1066            // update hovered if widgets moved under the cursor position.
1067            let mut frame_info = match WINDOWS.widget_tree(window_id) {
1068                Ok(f) => f,
1069                Err(_) => {
1070                    self.clean_all_state();
1071                    return;
1072                }
1073            };
1074            let mut pos_hits = frame_info.root().hit_test(self.pos.to_px(frame_info.scale_factor()));
1075            let mut position = self.pos;
1076            let target = if let Some(t) = pos_hits.target() {
1077                if let Some(w) = frame_info.get(t.widget_id) {
1078                    if let Some(f) = w.nested_window_tree() {
1079                        frame_info = f;
1080                        let factor = frame_info.scale_factor();
1081                        let pos = self.pos.to_px(factor);
1082                        let pos = w.inner_transform().inverse().and_then(|t| t.transform_point(pos)).unwrap_or(pos);
1083                        pos_hits = frame_info.root().hit_test(pos);
1084                        window_id = frame_info.window_id();
1085                        position = pos.to_dip(factor);
1086                        pos_hits
1087                            .target()
1088                            .and_then(|h| frame_info.get(h.widget_id))
1089                            .map(|w| w.interaction_path())
1090                            .unwrap_or_else(|| frame_info.root().interaction_path())
1091                    } else {
1092                        w.interaction_path()
1093                    }
1094                } else {
1095                    tracing::error!("hits target `{}` not found", t.widget_id);
1096                    frame_info.root().interaction_path()
1097                }
1098            } else {
1099                frame_info.root().interaction_path()
1100            }
1101            .unblocked();
1102            self.hits = Some(pos_hits.clone());
1103
1104            if self.hovered != target {
1105                let capture = POINTER_CAPTURE.current_capture_value();
1106                let prev = mem::replace(&mut self.hovered, target.clone());
1107                let args = MouseHoverArgs::now(window_id, None, position, pos_hits, prev, target, capture.clone(), capture);
1108                MOUSE_HOVERED_EVENT.notify(args);
1109            }
1110        }
1111    }
1112
1113    fn clean_all_state(&mut self) {
1114        let mouse = MOUSE_SV.read();
1115        if self.pos_window.take().is_some()
1116            && let Some(path) = self.hovered.take()
1117        {
1118            let window_id = path.window_id();
1119            mouse.buttons.with(|b| {
1120                for btn in b {
1121                    let args = MouseInputArgs::now(
1122                        window_id,
1123                        None,
1124                        *btn,
1125                        DipPoint::new(Dip::new(-1), Dip::new(-1)),
1126                        ModifiersState::empty(),
1127                        ButtonState::Released,
1128                        HitTestInfo::no_hits(window_id),
1129                        path.clone(),
1130                        None,
1131                        false,
1132                    );
1133                    MOUSE_INPUT_EVENT.notify(args);
1134                }
1135            });
1136
1137            let args = MouseHoverArgs::now(
1138                window_id,
1139                None,
1140                DipPoint::new(Dip::new(-1), Dip::new(-1)),
1141                HitTestInfo::no_hits(window_id),
1142                Some(path),
1143                None,
1144                None,
1145                None,
1146            );
1147            MOUSE_HOVERED_EVENT.notify(args);
1148        }
1149        mouse.buttons.set(vec![]);
1150        self.clicking.clear();
1151        self.pos_device = None;
1152        self.pos_window = None;
1153        self.hits = None;
1154        mouse.position.set(None);
1155        mouse.hovered.set(None);
1156    }
1157}
1158impl AppExtension for MouseManager {
1159    fn event_preview(&mut self, update: &mut EventUpdate) {
1160        if let Some(args) = RAW_FRAME_RENDERED_EVENT.on(update) {
1161            self.continue_hovered(args.window_id);
1162        } else if let Some(args) = RAW_MOUSE_MOVED_EVENT.on(update) {
1163            self.on_cursor_moved(args.window_id, args.device_id, args.coalesced_pos.clone(), args.position);
1164        } else if let Some(args) = RAW_MOUSE_WHEEL_EVENT.on(update) {
1165            self.on_scroll(args.window_id, args.device_id, args.delta, args.phase);
1166        } else if let Some(args) = RAW_MOUSE_INPUT_EVENT.on(update) {
1167            self.on_mouse_input(args.window_id, args.device_id, args.state, args.button);
1168        } else if let Some(args) = MODIFIERS_CHANGED_EVENT.on(update) {
1169            self.modifiers = args.modifiers;
1170        } else if let Some(args) = RAW_MOUSE_LEFT_EVENT.on(update) {
1171            self.on_cursor_left_window(args.window_id, args.device_id);
1172        } else if let Some(args) = RAW_WINDOW_FOCUS_EVENT.on(update) {
1173            if let Some(window_id) = args.prev_focus {
1174                self.on_window_blur(window_id, args.new_focus);
1175            }
1176        } else if let Some(args) = WIDGET_INFO_CHANGED_EVENT.on(update) {
1177            self.continue_hovered(args.window_id);
1178        } else if let Some(args) = RAW_MULTI_CLICK_CONFIG_CHANGED_EVENT.on(update) {
1179            MOUSE_SV.read().multi_click_config.set(args.config);
1180            self.clicking.clear();
1181        } else if let Some(args) = VIEW_PROCESS_INITED_EVENT.on(update)
1182            && args.is_respawn
1183        {
1184            self.clean_all_state();
1185        }
1186    }
1187
1188    fn event(&mut self, update: &mut EventUpdate) {
1189        if let Some(args) = POINTER_CAPTURE_EVENT.on(update)
1190            && let Some(path) = &self.hovered
1191            && self.pos_window.is_some()
1192        {
1193            let window_id = path.window_id();
1194            let hover_args = MouseHoverArgs::now(
1195                window_id,
1196                self.pos_device.unwrap(),
1197                self.pos,
1198                self.hits.clone().unwrap_or_else(|| HitTestInfo::no_hits(window_id)),
1199                Some(path.clone()),
1200                Some(path.clone()),
1201                args.prev_capture.clone(),
1202                args.new_capture.clone(),
1203            );
1204            MOUSE_HOVERED_EVENT.notify(hover_args);
1205        }
1206    }
1207
1208    fn update_preview(&mut self) {
1209        // update pressed repeat clicks
1210        for (btn, info) in self.clicking.iter_mut() {
1211            if let Some(timer) = info.repeat_timer.take() {
1212                // is repeat mode
1213                if timer.with_new(|t| t.has_elapsed()).unwrap_or(false) {
1214                    // time to repeat
1215                    info.repeat_count = info.repeat_count.saturating_add(1);
1216
1217                    if let Some(dv) = self.pos_device
1218                        && let Ok(tree) = WINDOWS.widget_tree(info.path.window_id())
1219                    {
1220                        // probably still valid
1221
1222                        let hit_test = tree.root().hit_test(self.pos.to_px(tree.scale_factor()));
1223
1224                        // get the hit target, constrained by capture
1225                        let mut target = None;
1226                        if let Some(hit) = hit_test.target().map(|t| tree.get(t.widget_id).unwrap()) {
1227                            target = hit.path().shared_ancestor(info.path.as_path()).map(|c| c.into_owned());
1228                        }
1229                        if let Some(c) = POINTER_CAPTURE.current_capture_value() {
1230                            match c.mode {
1231                                CaptureMode::Window => {
1232                                    if let Some(t) = &target {
1233                                        if t.window_id() != c.target.window_id() {
1234                                            target = None; // captured in other window, cancel repeat
1235                                        }
1236                                    } else {
1237                                        // no hit, but window capture
1238                                        target = Some(tree.root().path());
1239                                    }
1240                                }
1241                                CaptureMode::Subtree => {
1242                                    if let Some(t) = &target {
1243                                        target = c.target.shared_ancestor(t).map(|c| c.into_owned());
1244                                    } else {
1245                                        target = Some(c.target);
1246                                    }
1247                                }
1248                                CaptureMode::Widget => {
1249                                    target = Some(c.target);
1250                                }
1251                            }
1252                        }
1253
1254                        if let Some(target) = target {
1255                            // if still has a target
1256                            if let Some(target) = tree.get(target.widget_id()).and_then(|w| w.interaction_path().unblocked()) {
1257                                // and it is unblocked
1258
1259                                // notify repeat
1260                                let args = MouseClickArgs::now(
1261                                    target.window_id(),
1262                                    dv,
1263                                    *btn,
1264                                    self.pos,
1265                                    self.modifiers,
1266                                    NonZeroU32::new(info.repeat_count).unwrap(),
1267                                    true,
1268                                    hit_test,
1269                                    target,
1270                                );
1271                                MOUSE_CLICK_EVENT.notify(args);
1272
1273                                // continue timer
1274                                let t = MOUSE.repeat_config().get().interval;
1275                                info.repeat_timer = Some(TIMERS.deadline(t));
1276                            }
1277                        }
1278                    }
1279                } else {
1280                    // not time to repeat
1281                    info.repeat_timer = Some(timer);
1282                }
1283            }
1284        }
1285    }
1286}
1287
1288/// Represents mouse gestures that can initiate a click.
1289#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
1290pub enum ClickTrigger {
1291    /// Widget is clicked when the same mouse button is pressed and released on it.
1292    PressRelease,
1293    /// Widget is clicked when a mouse button is pressed on it.
1294    Press,
1295    /// Widget is clicked when a mouse button is released on it, even if not pressed on it.
1296    Release,
1297}
1298
1299/// Defines how click events are generated for a widget.
1300#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
1301pub struct ClickMode {
1302    /// Gesture that causes the *first* click, that is a click that is not in the double-click interval.
1303    pub single: ClickTrigger,
1304
1305    /// Gesture that causes the subsequent clicks, if done within the double-click interval.
1306    pub double: ClickTrigger,
1307
1308    /// If a mouse button is held pressed after a delay generate repeat clicks on an interval.
1309    pub repeat: bool,
1310}
1311impl Default for ClickMode {
1312    /// First `PressRelease`, double `Press` and no repeat.
1313    fn default() -> Self {
1314        Self {
1315            single: ClickTrigger::PressRelease,
1316            double: ClickTrigger::Press,
1317            repeat: false,
1318        }
1319    }
1320}
1321impl IntoVar<Option<ClickMode>> for ClickTrigger {
1322    fn into_var(self) -> Var<Option<ClickMode>> {
1323        Some(ClickMode::from(self)).into_var()
1324    }
1325}
1326impl_from_and_into_var! {
1327    fn from(gesture: ClickTrigger) -> ClickMode {
1328        ClickMode {
1329            single: gesture,
1330            double: gesture,
1331            repeat: false,
1332        }
1333    }
1334
1335    fn from(some: ClickMode) -> Option<ClickMode>;
1336}
1337impl ClickMode {
1338    /// Click on [`ClickTrigger::Press`].
1339    pub fn press() -> Self {
1340        Self {
1341            single: ClickTrigger::Press,
1342            double: ClickTrigger::Press,
1343            repeat: false,
1344        }
1345    }
1346
1347    /// Click on release.
1348    pub fn release() -> Self {
1349        Self {
1350            single: ClickTrigger::Release,
1351            double: ClickTrigger::Release,
1352            repeat: false,
1353        }
1354    }
1355
1356    /// Click on press and repeat.
1357    pub fn repeat() -> Self {
1358        Self {
1359            single: ClickTrigger::Press,
1360            double: ClickTrigger::Press,
1361            repeat: true,
1362        }
1363    }
1364
1365    /// Click on press+release or repeat.
1366    pub fn mixed_repeat() -> Self {
1367        Self {
1368            single: ClickTrigger::PressRelease,
1369            double: ClickTrigger::Press,
1370            repeat: true,
1371        }
1372    }
1373}
1374
1375/// Mouse config methods.
1376pub trait WidgetInfoMouseExt {
1377    /// Gets the click mode of the widget.
1378    fn click_mode(&self) -> ClickMode;
1379}
1380impl WidgetInfoMouseExt for WidgetInfo {
1381    fn click_mode(&self) -> ClickMode {
1382        for w in self.self_and_ancestors() {
1383            if let Some(m) = w.meta().get_clone(*CLICK_MODE_ID).flatten() {
1384                return m;
1385            }
1386        }
1387        ClickMode::default()
1388    }
1389}
1390
1391/// Mouse config builder methods.
1392pub trait WidgetInfoBuilderMouseExt {
1393    /// Sets the click mode of the widget.
1394    ///
1395    /// Setting this to `None` will cause the widget to inherit the click mode.
1396    fn set_click_mode(&mut self, mode: Option<ClickMode>);
1397}
1398impl WidgetInfoBuilderMouseExt for WidgetInfoBuilder {
1399    fn set_click_mode(&mut self, mode: Option<ClickMode>) {
1400        self.with_meta(|mut m| match m.entry(*CLICK_MODE_ID) {
1401            state_map::StateMapEntry::Occupied(mut e) => *e.get_mut() = mode,
1402            state_map::StateMapEntry::Vacant(e) => {
1403                if mode.is_some() {
1404                    e.insert(mode);
1405                }
1406            }
1407        })
1408    }
1409}
1410
1411static_id! {
1412    static ref CLICK_MODE_ID: StateId<Option<ClickMode>>;
1413}
1414
1415/// Settings that define the mouse button pressed repeat.
1416#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1417pub struct ButtonRepeatConfig {
1418    /// Delay before repeat starts.
1419    pub start_delay: Duration,
1420    /// Delay before each repeat event after the first.
1421    pub interval: Duration,
1422}
1423impl Default for ButtonRepeatConfig {
1424    /// 600ms, 100ms.
1425    fn default() -> Self {
1426        Self {
1427            start_delay: Duration::from_millis(600),
1428            interval: Duration::from_millis(100),
1429        }
1430    }
1431}
1432
1433/// Mouse service.
1434///
1435/// # Mouse Capture
1436///
1437/// Mouse capture is integrated with touch capture in the [`POINTER_CAPTURE`] service.
1438///
1439/// # Provider
1440///
1441/// This service is provided by the [`MouseManager`] extension, it will panic if used in an app not extended.
1442///
1443/// [`POINTER_CAPTURE`]: crate::pointer_capture::POINTER_CAPTURE
1444pub struct MOUSE;
1445impl MOUSE {
1446    /// Returns a read-only variable that tracks the [buttons] that are currently pressed.
1447    ///
1448    /// [buttons]: MouseButton
1449    pub fn buttons(&self) -> Var<Vec<MouseButton>> {
1450        MOUSE_SV.read().buttons.read_only()
1451    }
1452
1453    /// Variable that defines the click-count increment time and area, a.k.a. the double-click config.
1454    ///
1455    /// Repeated clicks with an interval less then this time and within the distance of the first click increment the click count.
1456    ///
1457    /// The value is the same as [`sys_multi_click_config`], if set the variable disconnects from system config.
1458    ///
1459    /// [`sys_multi_click_config`]: Self::sys_multi_click_config
1460    pub fn multi_click_config(&self) -> Var<MultiClickConfig> {
1461        MOUSE_SV.read().multi_click_config.clone()
1462    }
1463
1464    /// Variable that tracks the system click-count increment time and area, a.k.a. the double-click config.
1465    ///
1466    /// # Value Source
1467    ///
1468    /// The value comes from the operating system settings, the variable
1469    /// updates with a new value if the system setting is changed and on view-process (re)init.
1470    ///
1471    /// In headless apps the default is [`MultiClickConfig::default`] and does not change.
1472    pub fn sys_multi_click_config(&self) -> Var<MultiClickConfig> {
1473        MOUSE_SV.read().sys_multi_click_config.read_only()
1474    }
1475
1476    /// Variable that gets and sets the config for [`ClickMode::repeat`] clicks.
1477    ///
1478    /// Note that this variable is linked with [`KEYBOARD.repeat_config`] until it is set, so if it is never set
1479    /// it will update with the keyboard value.
1480    ///
1481    /// [`KEYBOARD.repeat_config`]: KEYBOARD::repeat_config
1482    pub fn repeat_config(&self) -> Var<ButtonRepeatConfig> {
1483        MOUSE_SV.read().repeat_config.clone()
1484    }
1485
1486    /// Variable that gets current hovered window and cursor point over that window.
1487    pub fn position(&self) -> Var<Option<MousePosition>> {
1488        MOUSE_SV.read().position.read_only()
1489    }
1490
1491    /// Variable that gets the current hovered window and widgets.
1492    pub fn hovered(&self) -> Var<Option<InteractionPath>> {
1493        MOUSE_SV.read().hovered.read_only()
1494    }
1495}
1496
1497/// Mouse cursor position.
1498///
1499/// Tracked in [`MOUSE.position`].
1500///
1501/// [`MOUSE.position`]: MOUSE::position
1502#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1503pub struct MousePosition {
1504    /// Window the mouse is hovering.
1505    pub window_id: WindowId,
1506    /// Mouse position in the window.
1507    pub position: DipPoint,
1508    /// Timestamp of the mouse move.
1509    pub timestamp: DInstant,
1510}
1511
1512app_local! {
1513    static MOUSE_SV: MouseService = {
1514        APP.extensions().require::<MouseManager>();
1515        let sys_multi_click_config = var(MultiClickConfig::default());
1516        MouseService {
1517            multi_click_config: sys_multi_click_config.cow(),
1518            sys_multi_click_config,
1519            repeat_config: KEYBOARD
1520                .repeat_config()
1521                .map(|c| ButtonRepeatConfig {
1522                    start_delay: c.start_delay,
1523                    interval: c.interval,
1524                })
1525                .cow(),
1526            buttons: var(vec![]),
1527            hovered: var(None),
1528            position: var(None),
1529        }
1530    };
1531}
1532struct MouseService {
1533    multi_click_config: Var<MultiClickConfig>,
1534    sys_multi_click_config: Var<MultiClickConfig>,
1535    repeat_config: Var<ButtonRepeatConfig>,
1536    buttons: Var<Vec<MouseButton>>,
1537    hovered: Var<Option<InteractionPath>>,
1538    position: Var<Option<MousePosition>>,
1539}