1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
//! Gesture events and control, [`on_click`](fn@on_click), [`click_shortcut`](fn@click_shortcut) and more.
//!
//! These events aggregate multiple lower-level events to represent a user interaction.
//! Prefer using these events over the events directly tied to an input device.

use zng_app::shortcut::Shortcuts;
use zng_ext_input::gesture::{ShortcutClick, CLICK_EVENT, GESTURES};
use zng_view_api::access::AccessCmdName;
use zng_wgt::prelude::*;

pub use zng_ext_input::gesture::ClickArgs;

event_property! {
    /// On widget click from any source and of any click count and the widget is enabled.
    ///
    /// This is the most general click handler, it raises for all possible sources of the [`CLICK_EVENT`] and any number
    /// 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)
    /// to not include double/triple clicks.
    ///
    /// [`CLICK_EVENT`]: zng_ext_input::gesture::CLICK_EVENT
    pub fn any_click {
        event: CLICK_EVENT,
        args: ClickArgs,
        filter: |args| args.is_enabled(WIDGET.id()),
        with: access_click,
    }

    /// On widget click from any source and of any click count and the widget is disabled.
    pub fn disabled_click {
        event: CLICK_EVENT,
        args: ClickArgs,
        filter: |args| args.is_disabled(WIDGET.id()),
        with: access_click,
    }

    /// On widget click from any source but excluding double/triple clicks and the widget is enabled.
    ///
    /// This raises for all possible sources of [`CLICK_EVENT`], but only when the click count is one. Use
    /// [`on_single_click`](fn@on_single_click) to handle only primary button clicks.
        ///
    /// [`CLICK_EVENT`]: zng_ext_input::gesture::CLICK_EVENT
    pub fn any_single_click {
        event: CLICK_EVENT,
        args: ClickArgs,
        filter: |args| args.is_single() && args.is_enabled(WIDGET.id()),
        with: access_click,
    }

    /// On widget double click from any source and the widget is enabled.
    ///
    /// This raises for all possible sources of [`CLICK_EVENT`], but only when the click count is two. Use
    /// [`on_double_click`](fn@on_double_click) to handle only primary button clicks.
        ///
    /// [`CLICK_EVENT`]: zng_ext_input::gesture::CLICK_EVENT
    pub fn any_double_click {
        event: CLICK_EVENT,
        args: ClickArgs,
        filter: |args| args.is_double() && args.is_enabled(WIDGET.id()),
    }

    /// On widget triple click from any source and the widget is enabled.
    ///
    /// This raises for all possible sources of [`CLICK_EVENT`], but only when the click count is three. Use
    /// [`on_triple_click`](fn@on_triple_click) to handle only primary button clicks.
        ///
    /// [`CLICK_EVENT`]: zng_ext_input::gesture::CLICK_EVENT
    pub fn any_triple_click {
        event: CLICK_EVENT,
        args: ClickArgs,
        filter: |args| args.is_triple() && args.is_enabled(WIDGET.id()),
    }

    /// On widget click with the primary button and any click count and the widget is enabled.
    ///
    /// This raises only if the click [is primary](ClickArgs::is_primary), but raises for any click count (double/triple clicks).
    /// 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
    /// double/triple clicks.
    pub fn click {
        event: CLICK_EVENT,
        args: ClickArgs,
        filter: |args| args.is_primary() && args.is_enabled(WIDGET.id()),
        with: access_click,
    }

    /// On widget click with the primary button, excluding double/triple clicks and the widget is enabled.
    ///
    /// This raises only if the click [is primary](ClickArgs::is_primary) and the click count is one. Use
    /// [`on_any_single_click`](fn@on_any_single_click) to handle single clicks from any button.
    pub fn single_click {
        event: CLICK_EVENT,
        args: ClickArgs,
        filter: |args| args.is_primary() && args.is_single() && args.is_enabled(WIDGET.id()),
        with: access_click,
    }

    /// On widget double click with the primary button and the widget is enabled.
    ///
    /// This raises only if the click [is primary](ClickArgs::is_primary) and the click count is two. Use
    /// [`on_any_double_click`](fn@on_any_double_click) to handle double clicks from any button.
    pub fn double_click {
        event: CLICK_EVENT,
        args: ClickArgs,
        filter: |args| args.is_primary() && args.is_double() && args.is_enabled(WIDGET.id()),
    }

    /// On widget triple click with the primary button and the widget is enabled.
    ///
    /// This raises only if the click [is primary](ClickArgs::is_primary) and the click count is three. Use
    /// [`on_any_double_click`](fn@on_any_double_click) to handle double clicks from any button.
    pub fn triple_click {
        event: CLICK_EVENT,
        args: ClickArgs,
        filter: |args| args.is_primary() && args.is_triple() && args.is_enabled(WIDGET.id()),
    }

    /// On widget click with the secondary/context button and the widget is enabled.
    ///
    /// This raises only if the click [is context](ClickArgs::is_context).
    pub fn context_click {
        event: CLICK_EVENT,
        args: ClickArgs,
        filter: |args| args.is_context() && args.is_enabled(WIDGET.id()),
        with: access_click,
    }
}

/// Keyboard shortcuts that focus and clicks this widget.
///
/// When any of the `shortcuts` is pressed, focus and click this widget.
#[property(CONTEXT)]
pub fn click_shortcut(child: impl UiNode, shortcuts: impl IntoVar<Shortcuts>) -> impl UiNode {
    click_shortcut_node(child, shortcuts, ShortcutClick::Primary)
}
/// Keyboard shortcuts that focus and [context clicks](fn@on_context_click) this widget.
///
/// When any of the `shortcuts` is pressed, focus and context clicks this widget.
#[property(CONTEXT)]
pub fn context_click_shortcut(child: impl UiNode, shortcuts: impl IntoVar<Shortcuts>) -> impl UiNode {
    click_shortcut_node(child, shortcuts, ShortcutClick::Context)
}

fn click_shortcut_node(child: impl UiNode, shortcuts: impl IntoVar<Shortcuts>, kind: ShortcutClick) -> impl UiNode {
    let shortcuts = shortcuts.into_var();
    let mut _handle = None;

    match_node(child, move |_, op| {
        let new = match op {
            UiNodeOp::Init => {
                WIDGET.sub_var(&shortcuts);
                Some(shortcuts.get())
            }
            UiNodeOp::Deinit => {
                _handle = None;
                None
            }
            UiNodeOp::Update { .. } => shortcuts.get_new(),
            _ => None,
        };
        if let Some(s) = new {
            _handle = Some(GESTURES.click_shortcut(s, kind, WIDGET.id()));
        }
    })
}

pub(crate) fn access_click(child: impl UiNode, _: bool) -> impl UiNode {
    access_capable(child, AccessCmdName::Click)
}
fn access_capable(child: impl UiNode, cmd: AccessCmdName) -> impl UiNode {
    match_node(child, move |_, op| {
        if let UiNodeOp::Info { info } = op {
            if let Some(mut access) = info.access() {
                access.push_command(cmd)
            }
        }
    })
}