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}