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 UiNode, mode: impl IntoVar<CaptureMode>) -> impl 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                    if let Some(current) = POINTER_CAPTURE.current_capture().get() {
79                        if current.target.widget_id() == widget_id {
80                            // If mode updated and we are capturing the mouse:
81                            match new_mode {
82                                CaptureMode::Widget => POINTER_CAPTURE.capture_widget(widget_id),
83                                CaptureMode::Subtree => POINTER_CAPTURE.capture_subtree(widget_id),
84                                CaptureMode::Window => POINTER_CAPTURE.release_capture(),
85                            }
86                        }
87                    }
88                }
89            }
90        }
91        _ => {}
92    })
93}
94
95/// Capture mouse and touch for the widget on init.
96///
97/// The capture happens only if any mouse button or touch is pressed on the window and the `mode` is [`Widget`] or [`Subtree`].
98///
99/// Pointer captures are released when all mouse buttons stop being pressed on the window.
100/// The capture is also released back to window if the `mode` changes to [`Window`] when the mouse is captured for the widget.
101///
102/// [`Widget`]: CaptureMode::Widget
103/// [`Subtree`]: CaptureMode::Subtree
104/// [`Window`]: CaptureMode::Window
105#[property(CONTEXT, default(false))]
106pub fn capture_pointer_on_init(child: impl UiNode, mode: impl IntoVar<CaptureMode>) -> impl UiNode {
107    let mode = mode.into_var();
108    let mut capture = true;
109
110    match_node(child, move |_, op| match op {
111        UiNodeOp::Init => {
112            WIDGET.sub_var(&mode);
113            capture = true; // wait for info
114        }
115        UiNodeOp::Info { .. } => {
116            if std::mem::take(&mut capture) {
117                let widget_id = WIDGET.id();
118
119                match mode.get() {
120                    CaptureMode::Widget => {
121                        POINTER_CAPTURE.capture_widget(widget_id);
122                    }
123                    CaptureMode::Subtree => {
124                        POINTER_CAPTURE.capture_subtree(widget_id);
125                    }
126                    CaptureMode::Window => (),
127                }
128            }
129        }
130        UiNodeOp::Update { .. } => {
131            if let Some(new_mode) = mode.get_new() {
132                let tree = WINDOW.info();
133                let widget_id = WIDGET.id();
134                if tree.get(widget_id).map(|w| w.interactivity().is_enabled()).unwrap_or(false) {
135                    if let Some(current) = POINTER_CAPTURE.current_capture().get() {
136                        if current.target.widget_id() == widget_id {
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        _ => {}
149    })
150}