zng_wgt_input/
gesture.rs

1//! Gesture events and control, [`on_click`](fn@on_click), [`click_shortcut`](fn@click_shortcut) and more.
2//!
3//! These events aggregate multiple lower-level events to represent a user interaction.
4//! Prefer using these events over the events directly tied to an input device.
5
6use zng_app::shortcut::Shortcuts;
7use zng_ext_input::gesture::{CLICK_EVENT, GESTURES, ShortcutClick};
8use zng_view_api::access::AccessCmdName;
9use zng_wgt::prelude::*;
10
11pub use zng_ext_input::gesture::ClickArgs;
12
13event_property! {
14    /// On widget click from any source and of any click count and the widget is enabled.
15    ///
16    /// This is the most general click handler, it raises for all possible sources of the [`CLICK_EVENT`] and any number
17    /// of consecutive clicks. Use [`on_click`](fn@on_click) to handle only primary button clicks or [`on_any_single_click`](fn@on_any_single_click)
18    /// to not include double/triple clicks.
19    ///
20    /// [`CLICK_EVENT`]: zng_ext_input::gesture::CLICK_EVENT
21    #[property(EVENT)]
22    pub fn on_any_click<on_pre_any_click>(child: impl IntoUiNode, handler: Handler<ClickArgs>) -> UiNode {
23        const PRE: bool;
24        let child = EventNodeBuilder::new(CLICK_EVENT)
25            .filter(|| {
26                let id = WIDGET.id();
27                move |args| args.target.contains_enabled(id)
28            })
29            .build::<PRE>(child, handler);
30        access_click(child)
31    }
32
33    /// On widget click from any source and of any click count and the widget is disabled.
34    #[property(EVENT)]
35    pub fn on_disabled_click<on_pre_disabled_click>(child: impl IntoUiNode, handler: Handler<ClickArgs>) -> UiNode {
36        const PRE: bool;
37        let child = EventNodeBuilder::new(CLICK_EVENT)
38            .filter(|| {
39                let id = WIDGET.id();
40                move |args| args.target.contains_disabled(id)
41            })
42            .build::<PRE>(child, handler);
43        access_click(child)
44    }
45
46    /// On widget click from any source but excluding double/triple clicks and the widget is enabled.
47    ///
48    /// This raises for all possible sources of [`CLICK_EVENT`], but only when the click count is one. Use
49    /// [`on_single_click`](fn@on_single_click) to handle only primary button clicks.
50    ///
51    /// [`CLICK_EVENT`]: zng_ext_input::gesture::CLICK_EVENT
52    #[property(EVENT)]
53    pub fn on_any_single_click<on_pre_any_single_click>(child: impl IntoUiNode, handler: Handler<ClickArgs>) -> UiNode {
54        const PRE: bool;
55        let child = EventNodeBuilder::new(CLICK_EVENT)
56            .filter(|| {
57                let id = WIDGET.id();
58                move |args| args.is_single() && args.target.contains_enabled(id)
59            })
60            .build::<PRE>(child, handler);
61        access_click(child)
62    }
63
64    /// On widget double click from any source and the widget is enabled.
65    ///
66    /// This raises for all possible sources of [`CLICK_EVENT`], but only when the click count is two. Use
67    /// [`on_double_click`](fn@on_double_click) to handle only primary button clicks.
68    ///
69    /// [`CLICK_EVENT`]: zng_ext_input::gesture::CLICK_EVENT
70    #[property(EVENT)]
71    pub fn on_any_double_click<on_pre_any_double_click>(child: impl IntoUiNode, handler: Handler<ClickArgs>) -> UiNode {
72        const PRE: bool;
73        EventNodeBuilder::new(CLICK_EVENT)
74            .filter(|| {
75                let id = WIDGET.id();
76                move |args| args.is_double() && args.target.contains_enabled(id)
77            })
78            .build::<PRE>(child, handler)
79    }
80
81    /// On widget triple click from any source and the widget is enabled.
82    ///
83    /// This raises for all possible sources of [`CLICK_EVENT`], but only when the click count is three. Use
84    /// [`on_triple_click`](fn@on_triple_click) to handle only primary button clicks.
85    ///
86    /// [`CLICK_EVENT`]: zng_ext_input::gesture::CLICK_EVENT
87    #[property(EVENT)]
88    pub fn on_any_triple_click<on_pre_any_triple_click>(child: impl IntoUiNode, handler: Handler<ClickArgs>) -> UiNode {
89        const PRE: bool;
90        EventNodeBuilder::new(CLICK_EVENT)
91            .filter(|| {
92                let id = WIDGET.id();
93                move |args| args.is_triple() && args.target.contains_enabled(id)
94            })
95            .build::<PRE>(child, handler)
96    }
97
98    /// On widget click with the primary button and any click count and the widget is enabled.
99    ///
100    /// This raises only if the click [is primary](ClickArgs::is_primary), but raises for any click count (double/triple clicks).
101    /// Use [`on_any_click`](fn@on_any_click) to handle clicks from any button or [`on_single_click`](fn@on_single_click) to not include
102    /// double/triple clicks.
103    #[property(EVENT)]
104    pub fn on_click<on_pre_click>(child: impl IntoUiNode, handler: Handler<ClickArgs>) -> UiNode {
105        const PRE: bool;
106        let child = EventNodeBuilder::new(CLICK_EVENT)
107            .filter(|| {
108                let id = WIDGET.id();
109                move |args| args.is_primary() && args.target.contains_enabled(id)
110            })
111            .build::<PRE>(child, handler);
112        access_click(child)
113    }
114
115    /// On widget click with the primary button, excluding double/triple clicks and the widget is enabled.
116    ///
117    /// This raises only if the click [is primary](ClickArgs::is_primary) and the click count is one. Use
118    /// [`on_any_single_click`](fn@on_any_single_click) to handle single clicks from any button.
119    #[property(EVENT)]
120    pub fn on_single_click<on_pre_single_click>(child: impl IntoUiNode, handler: Handler<ClickArgs>) -> UiNode {
121        const PRE: bool;
122        let child = EventNodeBuilder::new(CLICK_EVENT)
123            .filter(|| {
124                let id = WIDGET.id();
125                move |args| args.is_primary() && args.is_single() && args.target.contains_enabled(id)
126            })
127            .build::<PRE>(child, handler);
128        access_click(child)
129    }
130
131    /// On widget double click with the primary button and the widget is enabled.
132    ///
133    /// This raises only if the click [is primary](ClickArgs::is_primary) and the click count is two. Use
134    /// [`on_any_double_click`](fn@on_any_double_click) to handle double clicks from any button.
135    #[property(EVENT)]
136    pub fn on_double_click<on_pre_double_click>(child: impl IntoUiNode, handler: Handler<ClickArgs>) -> UiNode {
137        const PRE: bool;
138        EventNodeBuilder::new(CLICK_EVENT)
139            .filter(|| {
140                let id = WIDGET.id();
141                move |args| args.is_primary() && args.is_double() && args.target.contains_enabled(id)
142            })
143            .build::<PRE>(child, handler)
144    }
145
146    /// On widget triple click with the primary button and the widget is enabled.
147    ///
148    /// This raises only if the click [is primary](ClickArgs::is_primary) and the click count is three. Use
149    /// [`on_any_double_click`](fn@on_any_double_click) to handle double clicks from any button.
150    #[property(EVENT)]
151    pub fn on_triple_click<on_pre_triple_click>(child: impl IntoUiNode, handler: Handler<ClickArgs>) -> UiNode {
152        const PRE: bool;
153        EventNodeBuilder::new(CLICK_EVENT)
154            .filter(|| {
155                let id = WIDGET.id();
156                move |args| args.is_primary() && args.is_triple() && args.target.contains_enabled(id)
157            })
158            .build::<PRE>(child, handler)
159    }
160
161    /// On widget click with the secondary/context button and the widget is enabled.
162    ///
163    /// This raises only if the click [is context](ClickArgs::is_context).
164    #[property(EVENT)]
165    pub fn on_context_click<on_pre_context_click>(child: impl IntoUiNode, handler: Handler<ClickArgs>) -> UiNode {
166        const PRE: bool;
167        let child = EventNodeBuilder::new(CLICK_EVENT)
168            .filter(|| {
169                let id = WIDGET.id();
170                move |args| args.is_context() && args.target.contains_enabled(id)
171            })
172            .build::<PRE>(child, handler);
173        access_click(child)
174    }
175}
176
177/// Keyboard shortcuts that focus and clicks this widget.
178///
179/// When any of the `shortcuts` is pressed, focus and click this widget.
180#[property(CONTEXT)]
181pub fn click_shortcut(child: impl IntoUiNode, shortcuts: impl IntoVar<Shortcuts>) -> UiNode {
182    click_shortcut_node(child, shortcuts, ShortcutClick::Primary)
183}
184/// Keyboard shortcuts that focus and [context clicks](fn@on_context_click) this widget.
185///
186/// When any of the `shortcuts` is pressed, focus and context clicks this widget.
187#[property(CONTEXT)]
188pub fn context_click_shortcut(child: impl IntoUiNode, shortcuts: impl IntoVar<Shortcuts>) -> UiNode {
189    click_shortcut_node(child, shortcuts, ShortcutClick::Context)
190}
191
192fn click_shortcut_node(child: impl IntoUiNode, shortcuts: impl IntoVar<Shortcuts>, kind: ShortcutClick) -> UiNode {
193    let shortcuts = shortcuts.into_var();
194    let mut _handle = None;
195
196    match_node(child, move |_, op| {
197        let new = match op {
198            UiNodeOp::Init => {
199                WIDGET.sub_var(&shortcuts);
200                Some(shortcuts.get())
201            }
202            UiNodeOp::Deinit => {
203                _handle = None;
204                None
205            }
206            UiNodeOp::Update { .. } => shortcuts.get_new(),
207            _ => None,
208        };
209        if let Some(s) = new {
210            _handle = Some(GESTURES.click_shortcut(s, kind, WIDGET.id()));
211        }
212    })
213}
214
215pub(crate) fn access_click(child: impl IntoUiNode) -> UiNode {
216    access_capable(child, AccessCmdName::Click)
217}
218fn access_capable(child: impl IntoUiNode, cmd: AccessCmdName) -> UiNode {
219    match_node(child, move |_, op| {
220        if let UiNodeOp::Info { info } = op
221            && let Some(mut access) = info.access()
222        {
223            access.push_command(cmd)
224        }
225    })
226}