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