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    pub fn any_click {
22        event: CLICK_EVENT,
23        args: ClickArgs,
24        filter: |args| args.is_enabled(WIDGET.id()),
25        with: access_click,
26    }
27
28    /// On widget click from any source and of any click count and the widget is disabled.
29    pub fn disabled_click {
30        event: CLICK_EVENT,
31        args: ClickArgs,
32        filter: |args| args.is_disabled(WIDGET.id()),
33        with: access_click,
34    }
35
36    /// On widget click from any source but excluding double/triple clicks and the widget is enabled.
37    ///
38    /// This raises for all possible sources of [`CLICK_EVENT`], but only when the click count is one. Use
39    /// [`on_single_click`](fn@on_single_click) to handle only primary button clicks.
40        ///
41    /// [`CLICK_EVENT`]: zng_ext_input::gesture::CLICK_EVENT
42    pub fn any_single_click {
43        event: CLICK_EVENT,
44        args: ClickArgs,
45        filter: |args| args.is_single() && args.is_enabled(WIDGET.id()),
46        with: access_click,
47    }
48
49    /// On widget double click from any source and the widget is enabled.
50    ///
51    /// This raises for all possible sources of [`CLICK_EVENT`], but only when the click count is two. Use
52    /// [`on_double_click`](fn@on_double_click) to handle only primary button clicks.
53        ///
54    /// [`CLICK_EVENT`]: zng_ext_input::gesture::CLICK_EVENT
55    pub fn any_double_click {
56        event: CLICK_EVENT,
57        args: ClickArgs,
58        filter: |args| args.is_double() && args.is_enabled(WIDGET.id()),
59    }
60
61    /// On widget triple click from any source and the widget is enabled.
62    ///
63    /// This raises for all possible sources of [`CLICK_EVENT`], but only when the click count is three. Use
64    /// [`on_triple_click`](fn@on_triple_click) to handle only primary button clicks.
65        ///
66    /// [`CLICK_EVENT`]: zng_ext_input::gesture::CLICK_EVENT
67    pub fn any_triple_click {
68        event: CLICK_EVENT,
69        args: ClickArgs,
70        filter: |args| args.is_triple() && args.is_enabled(WIDGET.id()),
71    }
72
73    /// On widget click with the primary button and any click count and the widget is enabled.
74    ///
75    /// This raises only if the click [is primary](ClickArgs::is_primary), but raises for any click count (double/triple clicks).
76    /// 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
77    /// double/triple clicks.
78    pub fn click {
79        event: CLICK_EVENT,
80        args: ClickArgs,
81        filter: |args| args.is_primary() && args.is_enabled(WIDGET.id()),
82        with: access_click,
83    }
84
85    /// On widget click with the primary button, excluding double/triple clicks and the widget is enabled.
86    ///
87    /// This raises only if the click [is primary](ClickArgs::is_primary) and the click count is one. Use
88    /// [`on_any_single_click`](fn@on_any_single_click) to handle single clicks from any button.
89    pub fn single_click {
90        event: CLICK_EVENT,
91        args: ClickArgs,
92        filter: |args| args.is_primary() && args.is_single() && args.is_enabled(WIDGET.id()),
93        with: access_click,
94    }
95
96    /// On widget double click with the primary button and the widget is enabled.
97    ///
98    /// This raises only if the click [is primary](ClickArgs::is_primary) and the click count is two. Use
99    /// [`on_any_double_click`](fn@on_any_double_click) to handle double clicks from any button.
100    pub fn double_click {
101        event: CLICK_EVENT,
102        args: ClickArgs,
103        filter: |args| args.is_primary() && args.is_double() && args.is_enabled(WIDGET.id()),
104    }
105
106    /// On widget triple click with the primary button and the widget is enabled.
107    ///
108    /// This raises only if the click [is primary](ClickArgs::is_primary) and the click count is three. Use
109    /// [`on_any_double_click`](fn@on_any_double_click) to handle double clicks from any button.
110    pub fn triple_click {
111        event: CLICK_EVENT,
112        args: ClickArgs,
113        filter: |args| args.is_primary() && args.is_triple() && args.is_enabled(WIDGET.id()),
114    }
115
116    /// On widget click with the secondary/context button and the widget is enabled.
117    ///
118    /// This raises only if the click [is context](ClickArgs::is_context).
119    pub fn context_click {
120        event: CLICK_EVENT,
121        args: ClickArgs,
122        filter: |args| args.is_context() && args.is_enabled(WIDGET.id()),
123        with: access_click,
124    }
125}
126
127/// Keyboard shortcuts that focus and clicks this widget.
128///
129/// When any of the `shortcuts` is pressed, focus and click this widget.
130#[property(CONTEXT)]
131pub fn click_shortcut(child: impl UiNode, shortcuts: impl IntoVar<Shortcuts>) -> impl UiNode {
132    click_shortcut_node(child, shortcuts, ShortcutClick::Primary)
133}
134/// Keyboard shortcuts that focus and [context clicks](fn@on_context_click) this widget.
135///
136/// When any of the `shortcuts` is pressed, focus and context clicks this widget.
137#[property(CONTEXT)]
138pub fn context_click_shortcut(child: impl UiNode, shortcuts: impl IntoVar<Shortcuts>) -> impl UiNode {
139    click_shortcut_node(child, shortcuts, ShortcutClick::Context)
140}
141
142fn click_shortcut_node(child: impl UiNode, shortcuts: impl IntoVar<Shortcuts>, kind: ShortcutClick) -> impl UiNode {
143    let shortcuts = shortcuts.into_var();
144    let mut _handle = None;
145
146    match_node(child, move |_, op| {
147        let new = match op {
148            UiNodeOp::Init => {
149                WIDGET.sub_var(&shortcuts);
150                Some(shortcuts.get())
151            }
152            UiNodeOp::Deinit => {
153                _handle = None;
154                None
155            }
156            UiNodeOp::Update { .. } => shortcuts.get_new(),
157            _ => None,
158        };
159        if let Some(s) = new {
160            _handle = Some(GESTURES.click_shortcut(s, kind, WIDGET.id()));
161        }
162    })
163}
164
165pub(crate) fn access_click(child: impl UiNode, _: bool) -> impl UiNode {
166    access_capable(child, AccessCmdName::Click)
167}
168fn access_capable(child: impl UiNode, cmd: AccessCmdName) -> impl UiNode {
169    match_node(child, move |_, op| {
170        if let UiNodeOp::Info { info } = op {
171            if let Some(mut access) = info.access() {
172                access.push_command(cmd)
173            }
174        }
175    })
176}