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
177
178
179
180
//!
//! Builder for application shortcuts.
//!
//! # Synopsis
//! ```rust
//! use workflow_nw::prelude::*;
//! use workflow_nw::result::Result;
//! use workflow_dom::utils::window;
//!
//! # fn test()->Result<()>{
//!
//! let shortcut = ShortcutBuilder::new()
//!     .key("Ctrl+Shift+Q")
//!     .active(|_|{
//!         window().alert_with_message("Ctrl+Shift+Q pressed, App will close")?;
//!         //nw_sys::app::quit();
//!         nw_sys::app::close_all_windows();
//!         Ok(())
//!     })
//!     .build()?;
//!     
//! nw_sys::app::register_global_hot_key(&shortcut);
//!
//! # Ok(())
//! # }
//! ```
//!

use crate::application::app;
use crate::result::Result;
use nw_sys::prelude::*;
use wasm_bindgen::prelude::*;
use workflow_wasm::prelude::*;

/// Shortcut Info Object returned by [`ShortcutBuilder.finalize`](ShortcutBuilder#method.finalize) method
pub struct ShortcutInfo {
    pub shortcut: nw_sys::Shortcut,
    pub active_callback: Option<Callback<CallbackClosure<JsValue>>>,
    pub failed_callback: Option<Callback<CallbackClosure<JsValue>>>,
}

/// Provides a builder pattern for building application
/// keyboard shortcuts.
///
/// For usage example please refer to [Examples](self)
pub struct ShortcutBuilder {
    pub options: nw_sys::shortcut::Options,
    pub active_callback: Option<Callback<CallbackClosure<JsValue>>>,
    pub failed_callback: Option<Callback<CallbackClosure<JsValue>>>,
}

impl Default for ShortcutBuilder {
    fn default() -> Self {
        Self::new()
    }
}

impl ShortcutBuilder {
    pub fn new() -> Self {
        Self {
            options: nw_sys::shortcut::Options::new(),
            active_callback: None,
            failed_callback: None,
        }
    }

    fn set(mut self, key: &str, value: JsValue) -> Self {
        self.options = self.options.set(key, value);
        self
    }

    /// Set the `key` of a `Shortcut`.
    /// It is a string to specify the shortcut key, like "Ctrl+Alt+A".
    /// The key is consisted of zero or more modifiers and a key on your keyboard.
    /// Only one key code is supported. Key code is case insensitive.
    ///
    /// ### List of supported modifiers:
    ///
    /// - Ctrl
    /// - Alt
    /// - Shift
    /// - Command: Command modifier maps to Apple key (⌘) on Mac,
    /// and maps to the Windows key on Windows and Linux.
    ///
    /// ### List of supported keys:
    ///
    /// - Alphabet: `A`-`Z`
    /// - Digits: `0`-`9`
    /// - Function Keys: `F1`-`F24`
    /// - Home / End / PageUp / PageDown / Insert / Delete
    /// - Up / Down / Left / Right
    /// - MediaNextTrack / MediaPlayPause / MediaPrevTrack / MediaStop
    /// - Comma or `,`
    /// - Period or `.`
    /// - Tab or `\t`
    /// - Backquote or `` ` ``
    /// - Enter or `\n`
    /// - Minus or `-`
    /// - Equal or `=`
    /// - Backslash or `\`
    /// - Semicolon or `;`
    /// - Quote or `'`
    /// - BracketLeft or `[`
    /// - BracketRight or `]`
    /// - Escape
    ///
    ///
    /// ⧉ [NWJS Documentation](https://docs.nwjs.io/en/latest/References/Shortcut/#shortcutkey)
    pub fn key(self, key: &str) -> Self {
        self.set("key", JsValue::from(key))
    }

    /// Set the active callback of a Shortcut.
    /// It will be called when user presses the shortcut.
    ///
    /// ⧉ [NWJS Documentation](https://docs.nwjs.io/en/latest/References/Shortcut/#shortcutactive)
    pub fn active<F>(mut self, callback: F) -> Self
    where
        F: FnMut(JsValue) -> std::result::Result<(), JsValue> + 'static,
    {
        let callback = Callback::new(callback);
        self = self.set("active", callback.clone().into());
        self.active_callback = Some(callback);

        self
    }

    /// Set the failed callback of a Shortcut.
    /// It will be called when application passes an invalid key,
    /// or failed to register the key.
    ///
    /// ⧉ [NWJS Documentation](https://docs.nwjs.io/en/latest/References/Shortcut/#shortcutfailed)
    pub fn failed<F>(mut self, callback: F) -> Self
    where
        F: FnMut(JsValue) -> std::result::Result<(), JsValue> + 'static,
    {
        let callback = Callback::new(callback);
        self = self.set("failed", callback.clone().into());
        self.failed_callback = Some(callback);

        self
    }

    /// create [nw_sys::Shortcut](nw_sys::Shortcut) and
    /// return it
    ///
    pub fn build(self) -> Result<nw_sys::Shortcut> {
        if let Some(callback) = self.active_callback {
            let app = match app() {
                Some(app) => app,
                None => return Err("app is not initialized".to_string().into()),
            };
            app.callbacks.retain(callback)?;
        }
        if let Some(callback) = self.failed_callback {
            let app = match app() {
                Some(app) => app,
                None => return Err("app is not initialized".to_string().into()),
            };
            app.callbacks.retain(callback)?;
        }

        let shortcut = nw_sys::Shortcut::new(&self.options);
        Ok(shortcut)
    }

    /// create [nw_sys::Shortcut](nw_sys::Shortcut) and
    /// return it with
    /// [active_callback](Self#structfield.active_callback),
    /// [failed_callback](Self#structfield.failed_callback) handlers
    ///
    pub fn finalize(self) -> Result<ShortcutInfo> {
        let shortcut = nw_sys::Shortcut::new(&self.options);
        Ok(ShortcutInfo {
            shortcut,
            active_callback: self.active_callback,
            failed_callback: self.failed_callback,
        })
    }
}