Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion cli/tauri.js/api-src/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as process from './process'
import * as tauri from './tauri'
import * as window from './window'
import * as notification from './notification'
import * as globalShortcut from './global-shortcut'

export {
cli,
Expand All @@ -19,5 +20,6 @@ export {
process,
tauri,
window,
notification
notification,
globalShortcut
}
52 changes: 52 additions & 0 deletions cli/tauri.js/api-src/global-shortcut.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { invoke, transformCallback } from './tauri'

interface ShortcutHandler {
shortcut: string
handler: () => void
onError?: (error: string) => void
}

/**
* Shortcut manager
*/
class ShortcutManager {
handlers: ShortcutHandler[]

constructor() {
this.handlers = []
}

/**
* register a global shortcut
* @param shortcut shortcut definition, modifiers and key separated by "+" e.g. Control+Q
* @param handler shortcut handler callback
* @param onError shortcut error callback
*/
registerShortcut(shortcut: string, handler: () => void, onError?: (error: string) => void) {
this.handlers.push({
shortcut,
handler,
onError
})
}

/**
* notifies the backend that you're done registering shortcuts, and it should start listening
*/
listen() {
invoke({
cmd: 'addShortcuts',
shortcutHandlers: this.handlers.map(handler => {
return {
shortcut: handler.shortcut,
callback: transformCallback(handler.handler),
error: handler.onError ? transformCallback(handler.onError) : null
}
})
})
}
}

export {
ShortcutManager
}
1 change: 1 addition & 0 deletions cli/tauri.js/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export default [{
'window': './api-src/window.ts',
'cli': './api-src/cli.ts',
'notification': './api-src/notification.ts',
'global-shortcut': './api-src/global-shortcut.ts',
},
treeshake: true,
perf: true,
Expand Down
2 changes: 2 additions & 0 deletions tauri-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ tauri-utils = { version = "0.5", path = "../tauri-utils" }
clap = { git = "https://github.com/clap-rs/clap", rev = "1a276f8", version = "3.0.0-beta.1", optional = true }
notify-rust = { version = "4.0.0", optional = true }
once_cell = "1.4.0"
hotkey = { version = "0.3.1", optional = true }

[dev-dependencies]
quickcheck = "0.9.2"
Expand All @@ -44,3 +45,4 @@ quickcheck_macros = "0.9.1"
[features]
cli = [ "clap" ]
notification = [ "notify-rust" ]
shortcuts = ["hotkey"]
4 changes: 4 additions & 0 deletions tauri-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ pub use tauri_utils::*;
pub use anyhow::Result;
use thiserror::Error;

/// The `shortcuts` management module.
#[cfg(feature = "shortcuts")]
pub mod shortcuts;

/// The error types.
#[derive(Error, Debug)]
pub enum Error {
Expand Down
94 changes: 94 additions & 0 deletions tauri-api/src/shortcuts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use hotkey;

/// The shortcut manager builder.
pub struct ShortcutManager {
hk: hotkey::Listener,
}

impl Default for ShortcutManager {
fn default() -> Self {
let hk = hotkey::Listener::new();
Self { hk }
}
}

impl ShortcutManager {
/// Initializes a new instance of the shortcut manager.
pub fn new() -> Self {
Default::default()
}

/// Registers a new shortcut handler.
pub fn register_shortcut<H: Fn() + 'static, E: Fn(String)>(
&mut self,
shortcut: String,
handler: H,
error: E,
) {
let hk = &mut self.hk;

let mut shortcut_modifier: u32 = 0;
let mut shortcut_key: u32 = 0;

let mut modifiers = shortcut.split('+').peekable();
while let Some(key) = modifiers.next() {
if modifiers.peek().is_some() {
let hotkey_modifier = match key.to_uppercase().as_str() {
"ALT" => hotkey::modifiers::ALT,
"CONTROL" | "CTRL" => hotkey::modifiers::CONTROL,
"SHIFT" => hotkey::modifiers::SHIFT,
"SUPER" => hotkey::modifiers::SUPER,
_ => {
error(format!("unknown modifier {}", key));
return;
}
};
shortcut_modifier |= hotkey_modifier;
} else {
let hotkey_key = match key.to_uppercase().as_str() {
"BACKSPACE" => hotkey::keys::BACKSPACE,
"TAB" => hotkey::keys::TAB,
"ENTER" | "RETURN" => hotkey::keys::ENTER,
"CAPSLOCK" => hotkey::keys::CAPS_LOCK,
"ESCAPE" => hotkey::keys::ESCAPE,
"SPACEBAR" => hotkey::keys::SPACEBAR,
"PAGEUP" => hotkey::keys::PAGE_UP,
"PAGEDOWN" => hotkey::keys::PAGE_DOWN,
"END" => hotkey::keys::END,
"HOME" => hotkey::keys::HOME,
"LEFT" => hotkey::keys::ARROW_LEFT,
"RIGHT" => hotkey::keys::ARROW_RIGHT,
"UP" => hotkey::keys::ARROW_UP,
"DOWN" => hotkey::keys::ARROW_DOWN,
"PRINTSCREEN" => hotkey::keys::PRINT_SCREEN,
"INSERT" => hotkey::keys::INSERT,
"DELETE" => hotkey::keys::DELETE,
_ => {
let chars: Vec<char> = key.chars().collect();
if chars.len() != 1 {
error(format!(
"shortcut '{}' last element should be a character, found {}",
shortcut, key
));
return;
} else {
chars[0] as u32
}
}
};
shortcut_key = hotkey_key;
}
}

let hotkey_registration = hk.register_hotkey(shortcut_modifier, shortcut_key, handler);

if let Err(e) = hotkey_registration {
error(e);
}
}

/// Starts listening to the shortcuts.
pub fn listen(self) {
self.hk.listen();
}
}
3 changes: 2 additions & 1 deletion tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ cli = [ "tauri-api/cli" ]
edge = [ "tauri-web-view/edge" ]
embedded-server = [ "tiny_http" ]
no-server = [ ]
all-api = [ "tauri-api/notification" ]
all-api = [ "tauri-api/notification", "tauri-api/shortcuts" ]
read-text-file = [ ]
read-binary-file = [ ]
write-file = [ ]
Expand All @@ -72,6 +72,7 @@ updater = [ ]
open-dialog = [ ]
save-dialog = [ ]
notification = [ "tauri-api/notification" ]
shortcuts = [ "tauri-api/shortcuts" ]

[[example]]
name = "communication"
Expand Down
4 changes: 4 additions & 0 deletions tauri/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ fn shared() {
setup_env_aliases();
}

#[allow(clippy::cognitive_complexity)]
fn setup_env_aliases() {
cfg_aliases! {
embedded_server: { feature = "embedded-server" },
Expand Down Expand Up @@ -137,5 +138,8 @@ fn setup_env_aliases() {

// notification
notification: { any(all_api, feature = "notification") },

// shortcuts
shortcuts: { any(all_api, feature = "shortcuts") },
}
}
8 changes: 8 additions & 0 deletions tauri/src/endpoints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ mod event;
mod http;
#[cfg(notification)]
mod notification;
#[cfg(shortcuts)]
mod shortcuts;

use web_view::WebView;

Expand Down Expand Up @@ -230,6 +232,12 @@ pub(crate) fn handle<T: 'static>(webview: &mut WebView<'_, T>, arg: &str) -> cra
#[cfg(not(http_request))]
whitelist_error(webview, error, "httpRequest");
}
AddShortcuts { shortcut_handlers } => {
#[cfg(http_request)]
shortcuts::add_shortcuts(webview, shortcut_handlers);
#[cfg(not(http_request))]
whitelist_error(webview, error, "shortcuts");
}
#[cfg(assets)]
LoadAsset {
asset,
Expand Down
13 changes: 13 additions & 0 deletions tauri/src/endpoints/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ pub struct SaveDialogOptions {
pub default_path: Option<PathBuf>,
}

/// Shortcut handler options.
#[cfg(shortcuts)]
#[derive(Deserialize, Clone)]
pub struct ShortcutHandler {
pub shortcut: String,
pub callback: String,
pub error: Option<String>,
}

/// The options for the notification API.
#[derive(Deserialize)]
pub struct NotificationOptions {
Expand Down Expand Up @@ -188,6 +197,10 @@ pub enum Cmd {
},
/// The load asset into webview API.
#[serde(rename_all = "camelCase")]
AddShortcuts {
shortcut_handlers: Vec<ShortcutHandler>,
},
#[serde(rename_all = "camelCase")]
#[cfg(assets)]
LoadAsset {
asset: String,
Expand Down
71 changes: 71 additions & 0 deletions tauri/src/endpoints/shortcuts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use super::cmd::ShortcutHandler;
use tauri_api::shortcuts::ShortcutManager;
use web_view::WebView;

use std::sync::Arc;

pub fn add_shortcuts<T: 'static>(
webview: &mut WebView<'_, T>,
shortcut_handlers: Vec<ShortcutHandler>,
) {
let callback_handle = webview.handle();
let error_handle = webview.handle();
let callback_handle = Arc::new(callback_handle);

crate::spawn(move || {
let mut manager = ShortcutManager::new();
for shortcut_handler in shortcut_handlers {
let callback_handle = callback_handle.clone();

let callback_identifier = shortcut_handler.callback.clone();
let callback_identifier = Arc::new(callback_identifier);

manager.register_shortcut(
shortcut_handler.shortcut.clone(),
move || {
let callback_handle = callback_handle.clone();
let callback_string =
tauri_api::rpc::format_callback(callback_identifier.to_string(), "void 0".to_string());
callback_handle
.dispatch(move |_webview| _webview.eval(callback_string.as_str()))
.expect("Failed to dispatch shortcut callback");
},
|e| {
if let Some(error) = &shortcut_handler.error {
let callback_string = tauri_api::rpc::format_callback(error.to_string(), e);
error_handle
.dispatch(move |_webview| _webview.eval(callback_string.as_str()))
.expect("Failed to dispatch shortcut error");
}
},
);
}
manager.listen();
});

/*let handle = webview.handle();
let handle = std::sync::Arc::new(handle);
let j = shortcut_handlers.clone();
crate::spawn(move || {
let handlers: Vec<ShortcutHandler> = j.iter().map(|handler| {
let handle = handle.clone();
ShortcutHandler {
shortcut: handler.shortcut.clone(),
callback: Box::new(move || {
let callback_string = tauri_api::rpc::format_callback(handler.callback.clone(), "void 0".to_string());
handle
.dispatch(move |_webview| _webview.eval(callback_string.as_str()))
.expect("Failed to dispatch shortcut callback");
}),
error: Box::new(|e| {
/*lif let Some(error) = &handler.error {
let callback_string = tauri_api::rpc::format_callback(error, e);
handle
.dispatch(move |_webview| _webview.eval(callback_string.as_str()))
.expect("Failed to dispatch shortcut callback");
}*/
})
}
}).collect();
});*/
}