zng_wgt_input/
pointer_capture.rs

1//! Mouse and touch capture properties.
2
3use zng_ext_input::{
4    mouse::MOUSE_INPUT_EVENT,
5    pointer_capture::{POINTER_CAPTURE, POINTER_CAPTURE_EVENT, PointerCaptureArgs},
6    touch::TOUCH_INPUT_EVENT,
7};
8use zng_wgt::prelude::*;
9
10pub use zng_ext_input::pointer_capture::CaptureMode;
11
12event_property! {
13    /// Widget acquired mouse and touch capture.
14    #[property(EVENT)]
15    pub fn on_got_pointer_capture<on_pre_got_pointer_capture>(child: impl IntoUiNode, handler: Handler<PointerCaptureArgs>) -> UiNode {
16        const PRE: bool;
17        EventNodeBuilder::new(POINTER_CAPTURE_EVENT)
18            .filter(|| {
19                let id = WIDGET.id();
20                move |args| args.is_got(id)
21            })
22            .build::<PRE>(child, handler)
23    }
24
25    /// Widget lost mouse and touch capture.
26    #[property(EVENT)]
27    pub fn on_lost_pointer_capture<on_pre_lost_pointer_capture>(
28        child: impl IntoUiNode,
29        handler: Handler<PointerCaptureArgs>,
30    ) -> UiNode {
31        const PRE: bool;
32        EventNodeBuilder::new(POINTER_CAPTURE_EVENT)
33            .filter(|| {
34                let id = WIDGET.id();
35                move |args| args.is_lost(id)
36            })
37            .build::<PRE>(child, handler)
38    }
39
40    /// Widget acquired or lost mouse and touch capture.
41    #[property(EVENT)]
42    pub fn on_pointer_capture_changed<on_pre_pointer_capture_changed>(
43        child: impl IntoUiNode,
44        handler: Handler<PointerCaptureArgs>,
45    ) -> UiNode {
46        const PRE: bool;
47        EventNodeBuilder::new(POINTER_CAPTURE_EVENT).build::<PRE>(child, handler)
48    }
49}
50
51/// Capture mouse and touch for the widget on press.
52///
53/// The capture happens only if any mouse button or touch is pressed on the window and the `mode` is [`Widget`] or [`Subtree`].
54///
55/// Captures are released when all mouse buttons and touch contacts stop being pressed on the window.
56/// The capture is also released back to window if the `mode` changes to [`Window`].
57///
58/// [`Widget`]: CaptureMode::Widget
59/// [`Subtree`]: CaptureMode::Subtree
60/// [`Window`]: CaptureMode::Window
61#[property(CONTEXT, default(false))]
62pub fn capture_pointer(child: impl IntoUiNode, mode: impl IntoVar<CaptureMode>) -> UiNode {
63    let mode = mode.into_var();
64    match_node(child, move |_, op| match op {
65        UiNodeOp::Init => {
66            WIDGET.sub_event(&MOUSE_INPUT_EVENT).sub_event(&TOUCH_INPUT_EVENT).sub_var(&mode);
67        }
68        UiNodeOp::Update { .. } => {
69            if let Some(new_mode) = mode.get_new() {
70                let tree = WINDOW.info();
71                let widget_id = WIDGET.id();
72                if tree.get(widget_id).map(|w| w.interactivity().is_enabled()).unwrap_or(false)
73                    && let Some(current) = POINTER_CAPTURE.current_capture().get()
74                    && current.target.widget_id() == widget_id
75                {
76                    // If mode updated and we are capturing the mouse:
77                    match new_mode {
78                        CaptureMode::Widget => POINTER_CAPTURE.capture_widget(widget_id),
79                        CaptureMode::Subtree => POINTER_CAPTURE.capture_subtree(widget_id),
80                        CaptureMode::Window => POINTER_CAPTURE.release_capture(),
81                    }
82                }
83            }
84            if MOUSE_INPUT_EVENT.latest_update(true, |a| a.is_mouse_down()).unwrap_or(false)
85                || TOUCH_INPUT_EVENT.latest_update(true, |a| a.is_touch_start()).unwrap_or(false)
86            {
87                let widget_id = WIDGET.id();
88
89                match mode.get() {
90                    CaptureMode::Widget => {
91                        POINTER_CAPTURE.capture_widget(widget_id);
92                    }
93                    CaptureMode::Subtree => {
94                        POINTER_CAPTURE.capture_subtree(widget_id);
95                    }
96                    CaptureMode::Window => (),
97                }
98            }
99        }
100        _ => {}
101    })
102}
103
104/// Capture mouse and touch for the widget on init.
105///
106/// The capture happens only if any mouse button or touch is pressed on the window and the `mode` is [`Widget`] or [`Subtree`].
107///
108/// Pointer captures are released when all mouse buttons stop being pressed on the window.
109/// The capture is also released back to window if the `mode` changes to [`Window`] when the mouse is captured for the widget.
110///
111/// [`Widget`]: CaptureMode::Widget
112/// [`Subtree`]: CaptureMode::Subtree
113/// [`Window`]: CaptureMode::Window
114#[property(CONTEXT, default(false))]
115pub fn capture_pointer_on_init(child: impl IntoUiNode, mode: impl IntoVar<CaptureMode>) -> UiNode {
116    let mode = mode.into_var();
117    let mut capture = true;
118
119    match_node(child, move |_, op| match op {
120        UiNodeOp::Init => {
121            WIDGET.sub_var(&mode);
122            capture = true; // wait for info
123        }
124        UiNodeOp::Info { .. } => {
125            if std::mem::take(&mut capture) {
126                let widget_id = WIDGET.id();
127
128                match mode.get() {
129                    CaptureMode::Widget => {
130                        POINTER_CAPTURE.capture_widget(widget_id);
131                    }
132                    CaptureMode::Subtree => {
133                        POINTER_CAPTURE.capture_subtree(widget_id);
134                    }
135                    CaptureMode::Window => (),
136                }
137            }
138        }
139        UiNodeOp::Update { .. } => {
140            if let Some(new_mode) = mode.get_new() {
141                let tree = WINDOW.info();
142                let widget_id = WIDGET.id();
143                if tree.get(widget_id).map(|w| w.interactivity().is_enabled()).unwrap_or(false)
144                    && let Some(current) = POINTER_CAPTURE.current_capture().get()
145                    && current.target.widget_id() == widget_id
146                {
147                    // If mode updated and we are capturing the mouse:
148                    match new_mode {
149                        CaptureMode::Widget => POINTER_CAPTURE.capture_widget(widget_id),
150                        CaptureMode::Subtree => POINTER_CAPTURE.capture_subtree(widget_id),
151                        CaptureMode::Window => POINTER_CAPTURE.release_capture(),
152                    }
153                }
154            }
155        }
156        _ => {}
157    })
158}