feat(core): globalShortcut API (#1232)

This commit is contained in:
Lucas Fernandes Nogueira
2021-02-14 17:34:23 -03:00
committed by GitHub
parent 772d83e8fd
commit 855effadd9
22 changed files with 481 additions and 15 deletions

7
.changes/shortcut-api.md Normal file
View File

@@ -0,0 +1,7 @@
---
"api": minor
"tauri-api": minor
"tauri": minor
---
Adds a global shortcut API.

View File

@@ -14,7 +14,8 @@
"./notification": "./dist/notification.js",
"./tauri": "./dist/tauri.js",
"./window": "./dist/window.js",
"./shell": "./dist/shell.js"
"./shell": "./dist/shell.js",
"./globalShortcut": "./dist/globalShortcut.js"
},
"funding": {
"type": "opencollective",

View File

@@ -20,7 +20,8 @@ export default [
tauri: './src/tauri.ts',
window: './src/window.ts',
cli: './src/cli.ts',
notification: './src/notification.ts'
notification: './src/notification.ts',
globalShortcut: './src/globalShortcut.ts'
},
treeshake: true,
perf: true,

View File

@@ -9,6 +9,7 @@ import * as shell from './shell'
import * as tauri from './tauri'
import * as window from './window'
import * as notification from './notification'
import * as globalShortcut from './globalShortcut'
export {
cli,
@@ -20,5 +21,6 @@ export {
shell,
tauri,
window,
notification
notification,
globalShortcut
}

39
api/src/globalShortcut.ts Normal file
View File

@@ -0,0 +1,39 @@
import { promisified, transformCallback } from './tauri'
/**
* register a global shortcut
* @param shortcut shortcut definition, modifiers and key separated by "+" e.g. Alt+Q
* @param handler shortcut handler callback
*/
async function registerShortcut(
shortcut: string,
handler: () => void
): Promise<void> {
return await promisified({
module: 'GlobalShortcut',
message: {
cmd: 'register',
shortcut,
handler: transformCallback(handler)
}
})
}
/**
* unregister a global shortcut
* @param shortcut shortcut definition, modifiers and key separated by "+" e.g. Alt+Q
*/
async function unregisterShortcut(shortcut: string): Promise<void> {
return await promisified({
module: 'GlobalShortcut',
message: {
cmd: 'unregister',
shortcut
}
})
}
export {
registerShortcut,
unregisterShortcut
}

View File

@@ -155,11 +155,4 @@ async function deleteRequest<T>(
})
}
export {
request,
get,
post,
put,
patch,
deleteRequest as httpDelete,
}
export { request, get, post, put, patch, deleteRequest as httpDelete }

View File

@@ -187,7 +187,7 @@ function resize(width: number, height: number): void {
message: {
cmd: 'resize',
width,
height,
height
}
})
}

View File

@@ -35,6 +35,7 @@ tauri-utils = { version = "0.5", path = "../tauri-utils" }
clap = { version = "=3.0.0-beta.2", optional = true }
notify-rust = { version = "4.2.2", optional = true }
once_cell = "1.5.2"
tauri-hotkey = { git = "https://github.com/tauri-apps/tauri-hotkey-rs", branch = "dev", optional = true }
[dev-dependencies]
quickcheck = "1.0.3"
@@ -43,3 +44,4 @@ quickcheck_macros = "1.0.0"
[features]
cli = [ "clap" ]
notification = [ "notify-rust" ]
global-shortcut = [ "tauri-hotkey" ]

View File

@@ -53,6 +53,10 @@ pub enum Error {
#[cfg(feature = "cli")]
#[error("failed to parse CLI arguments: {0}")]
ParseCliArguments(#[from] clap::Error),
/// Shortcut error.
#[cfg(feature = "global-shortcut")]
#[error("shortcut error: {0}")]
Shortcut(#[from] tauri_hotkey::Error),
}
impl From<attohttpc::StatusCode> for Error {

View File

@@ -30,6 +30,10 @@ pub mod cli;
#[macro_use]
extern crate clap;
/// Global shortcuts interface.
#[cfg(feature = "global-shortcut")]
pub mod shortcuts;
/// The desktop notifications API module.
#[cfg(feature = "notification")]
pub mod notification;

View File

@@ -0,0 +1,30 @@
use tauri_hotkey::{parse_hotkey, HotkeyManager};
/// The shortcut manager builder.
#[derive(Default)]
pub struct ShortcutManager(HotkeyManager);
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: FnMut() + Send + 'static>(
&mut self,
shortcut: String,
handler: H,
) -> crate::Result<()> {
let hotkey = parse_hotkey(&shortcut.to_uppercase().replace(" ", ""))?;
self.0.register(hotkey, handler)?;
Ok(())
}
/// Unregister a previously registered shortcut handler.
pub fn unregister_shortcut(&mut self, shortcut: String) -> crate::Result<()> {
let hotkey = parse_hotkey(&shortcut.to_uppercase().replace(" ", ""))?;
self.0.unregister(&hotkey)?;
Ok(())
}
}

View File

@@ -49,7 +49,7 @@ serde = { version = "1.0", features = [ "derive" ] }
[features]
cli = [ "tauri-api/cli" ]
embedded-server = [ "tiny_http" ]
all-api = [ "tauri-api/notification" ]
all-api = [ "tauri-api/notification", "tauri-api/global-shortcut" ]
updater = [ ]
# FS
@@ -83,6 +83,9 @@ http-request = [ ]
# notification
notification = [ "tauri-api/notification" ]
# global shortcut
global-shortcut = [ "tauri-api/global-shortcut" ]
[[example]]
name = "communication"
path = "examples/communication/src-tauri/src/main.rs"

View File

@@ -44,5 +44,8 @@ fn main() {
// notification
notification: { any(all_api, feature = "notification") },
// global shortcut
global_shortcut: { any(all_api, feature = "global_shortcut" )},
}
}

View File

@@ -27,6 +27,15 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
[[package]]
name = "aho-corasick"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
dependencies = [
"memchr",
]
[[package]]
name = "andrew"
version = "0.3.1"
@@ -2105,6 +2114,24 @@ dependencies = [
"redox_syscall 0.2.4",
]
[[package]]
name = "regex"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
"thread_local",
]
[[package]]
name = "regex-syntax"
version = "0.6.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
[[package]]
name = "remove_dir_all"
version = "0.5.3"
@@ -2378,6 +2405,12 @@ version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b"
[[package]]
name = "strum"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7318c509b5ba57f18533982607f24070a55d353e90d4cae30c467cdb2ad5ac5c"
[[package]]
name = "strum_macros"
version = "0.8.0"
@@ -2400,6 +2433,18 @@ dependencies = [
"syn 1.0.60",
]
[[package]]
name = "strum_macros"
version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee8bc6b87a5112aeeab1f4a9f7ab634fe6cbefc4850006df31267f4cfb9e3149"
dependencies = [
"heck",
"proc-macro2",
"quote 1.0.8",
"syn 1.0.60",
]
[[package]]
name = "syn"
version = "0.11.11"
@@ -2515,6 +2560,7 @@ dependencies = [
"serde_repr",
"tar",
"tauri-dialog",
"tauri-hotkey",
"tauri-utils",
"tempfile",
"thiserror",
@@ -2540,6 +2586,32 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "tauri-hotkey"
version = "0.0.0"
source = "git+https://github.com/tauri-apps/tauri-hotkey-rs?branch=dev#48cda6dc66fbfc6f54c1aca3c14df9490f898729"
dependencies = [
"log",
"once_cell",
"regex",
"serde",
"strum 0.20.0",
"strum_macros 0.20.1",
"tauri-hotkey-sys",
"thiserror",
]
[[package]]
name = "tauri-hotkey-sys"
version = "0.0.0"
source = "git+https://github.com/tauri-apps/tauri-hotkey-rs?branch=dev#48cda6dc66fbfc6f54c1aca3c14df9490f898729"
dependencies = [
"cc",
"thiserror",
"winapi 0.3.9",
"x11-dl",
]
[[package]]
name = "tauri-macros"
version = "0.1.0"
@@ -2618,6 +2690,15 @@ dependencies = [
"syn 1.0.60",
]
[[package]]
name = "thread_local"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd"
dependencies = [
"once_cell",
]
[[package]]
name = "tiff"
version = "0.6.1"

View File

@@ -9,6 +9,7 @@
import Http from './components/Http.svelte'
import Notifications from './components/Notifications.svelte'
import Window from './components/Window.svelte'
import Shortcuts from './components/Shortcuts.svelte'
const views = [{
label: 'Messages',
@@ -31,6 +32,9 @@
}, {
label: 'Window',
component: Window
}, {
label: 'Shortcuts',
component: Shortcuts
}]
let selected = views[0].label;

View File

@@ -0,0 +1,41 @@
<script>
import { writable } from 'svelte/store'
import { registerShortcut, unregisterShortcut } from '@tauri-apps/api/globalShortcut'
export let onMessage
const shortcuts = writable([])
let shortcut = 'CTRL+X'
function register() {
const shortcut_ = shortcut
registerShortcut(shortcut_, () => {
onMessage(`Shortcut ${shortcut_} triggered`)
}).then(() => {
shortcuts.update(shortcuts_ => [...shortcuts_, shortcut_])
onMessage(`Shortcut ${shortcut_} registered successfully`)
}).catch(onMessage)
}
function unregister(shortcut) {
const shortcut_ = shortcut
unregisterShortcut(shortcut_).then(() => {
shortcuts.update(shortcuts_ => shortcuts_.filter(s => s !== shortcut_))
onMessage(`Shortcut ${shortcut_} unregistered`)
}).catch(onMessage)
}
</script>
<div style="margin-top: 24px">
<div>
<input placeholder="Type a shortcut with '+' as separator..." bind:value={shortcut}>
<button type="button" on:click={register}>Register</button>
</div>
<div>
{#each $shortcuts as savedShortcut}
<div>
{savedShortcut}
<button type="button" on:click={()=> unregister(savedShortcut)}>Unregister</button>
</div>
{/each}
</div>
</div>

File diff suppressed because one or more lines are too long

View File

@@ -27,6 +27,15 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
[[package]]
name = "aho-corasick"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
dependencies = [
"memchr",
]
[[package]]
name = "andrew"
version = "0.3.1"
@@ -2105,6 +2114,24 @@ dependencies = [
"redox_syscall 0.2.4",
]
[[package]]
name = "regex"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
"thread_local",
]
[[package]]
name = "regex-syntax"
version = "0.6.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
[[package]]
name = "remove_dir_all"
version = "0.5.3"
@@ -2378,6 +2405,12 @@ version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b"
[[package]]
name = "strum"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7318c509b5ba57f18533982607f24070a55d353e90d4cae30c467cdb2ad5ac5c"
[[package]]
name = "strum_macros"
version = "0.8.0"
@@ -2400,6 +2433,18 @@ dependencies = [
"syn 1.0.60",
]
[[package]]
name = "strum_macros"
version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee8bc6b87a5112aeeab1f4a9f7ab634fe6cbefc4850006df31267f4cfb9e3149"
dependencies = [
"heck",
"proc-macro2",
"quote 1.0.8",
"syn 1.0.60",
]
[[package]]
name = "syn"
version = "0.11.11"
@@ -2515,6 +2560,7 @@ dependencies = [
"serde_repr",
"tar",
"tauri-dialog",
"tauri-hotkey",
"tauri-utils",
"tempfile",
"thiserror",
@@ -2540,6 +2586,32 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "tauri-hotkey"
version = "0.0.0"
source = "git+https://github.com/tauri-apps/tauri-hotkey-rs?branch=dev#48cda6dc66fbfc6f54c1aca3c14df9490f898729"
dependencies = [
"log",
"once_cell",
"regex",
"serde",
"strum 0.20.0",
"strum_macros 0.20.1",
"tauri-hotkey-sys",
"thiserror",
]
[[package]]
name = "tauri-hotkey-sys"
version = "0.0.0"
source = "git+https://github.com/tauri-apps/tauri-hotkey-rs?branch=dev#48cda6dc66fbfc6f54c1aca3c14df9490f898729"
dependencies = [
"cc",
"thiserror",
"winapi 0.3.9",
"x11-dl",
]
[[package]]
name = "tauri-macros"
version = "0.1.0"
@@ -2618,6 +2690,15 @@ dependencies = [
"syn 1.0.60",
]
[[package]]
name = "thread_local"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd"
dependencies = [
"once_cell",
]
[[package]]
name = "tiff"
version = "0.6.1"

File diff suppressed because one or more lines are too long

View File

@@ -27,6 +27,15 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
[[package]]
name = "aho-corasick"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
dependencies = [
"memchr",
]
[[package]]
name = "andrew"
version = "0.3.1"
@@ -2038,6 +2047,24 @@ dependencies = [
"redox_syscall 0.2.4",
]
[[package]]
name = "regex"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
"thread_local",
]
[[package]]
name = "regex-syntax"
version = "0.6.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
[[package]]
name = "remove_dir_all"
version = "0.5.3"
@@ -2305,6 +2332,12 @@ version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b"
[[package]]
name = "strum"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7318c509b5ba57f18533982607f24070a55d353e90d4cae30c467cdb2ad5ac5c"
[[package]]
name = "strum_macros"
version = "0.8.0"
@@ -2327,6 +2360,18 @@ dependencies = [
"syn 1.0.60",
]
[[package]]
name = "strum_macros"
version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee8bc6b87a5112aeeab1f4a9f7ab634fe6cbefc4850006df31267f4cfb9e3149"
dependencies = [
"heck",
"proc-macro2",
"quote 1.0.8",
"syn 1.0.60",
]
[[package]]
name = "syn"
version = "0.11.11"
@@ -2441,6 +2486,7 @@ dependencies = [
"serde_repr",
"tar",
"tauri-dialog",
"tauri-hotkey",
"tauri-utils",
"tempfile",
"thiserror",
@@ -2466,6 +2512,32 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "tauri-hotkey"
version = "0.0.0"
source = "git+https://github.com/tauri-apps/tauri-hotkey-rs?branch=dev#48cda6dc66fbfc6f54c1aca3c14df9490f898729"
dependencies = [
"log",
"once_cell",
"regex",
"serde",
"strum 0.20.0",
"strum_macros 0.20.1",
"tauri-hotkey-sys",
"thiserror",
]
[[package]]
name = "tauri-hotkey-sys"
version = "0.0.0"
source = "git+https://github.com/tauri-apps/tauri-hotkey-rs?branch=dev#48cda6dc66fbfc6f54c1aca3c14df9490f898729"
dependencies = [
"cc",
"thiserror",
"winapi 0.3.9",
"x11-dl",
]
[[package]]
name = "tauri-macros"
version = "0.1.0"
@@ -2526,6 +2598,15 @@ dependencies = [
"syn 1.0.60",
]
[[package]]
name = "thread_local"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd"
dependencies = [
"once_cell",
]
[[package]]
name = "tiff"
version = "0.6.1"

View File

@@ -4,6 +4,8 @@ mod dialog;
mod event;
#[allow(unused_imports)]
mod file_system;
#[cfg(global_shortcut)]
mod global_shortcut;
#[cfg(http_request)]
mod http;
mod internal;
@@ -37,6 +39,7 @@ enum Module {
Cli(cli::Cmd),
Notification(notification::Cmd),
Http(http::Cmd),
GlobalShortcut(global_shortcut::Cmd),
}
impl Module {
@@ -55,6 +58,7 @@ impl Module {
Self::Cli(cmd) => cmd.run(webview_manager, context).await,
Self::Notification(cmd) => cmd.run(webview_manager, context).await?,
Self::Http(cmd) => cmd.run(webview_manager).await,
Self::GlobalShortcut(cmd) => cmd.run(webview_manager).await?,
}
Ok(())
}

View File

@@ -0,0 +1,85 @@
use crate::{api::shortcuts::ShortcutManager, async_runtime::Mutex};
use once_cell::sync::Lazy;
use serde::Deserialize;
use std::sync::Arc;
type ShortcutManagerHandle = Arc<Mutex<ShortcutManager>>;
pub fn manager_handle() -> &'static ShortcutManagerHandle {
static MANAGER: Lazy<ShortcutManagerHandle> = Lazy::new(Default::default);
&MANAGER
}
/// The API descriptor.
#[derive(Deserialize)]
#[serde(tag = "cmd", rename_all = "camelCase")]
pub enum Cmd {
/// Register a global shortcut.
Register {
shortcut: String,
handler: String,
callback: String,
error: String,
},
/// Unregister a global shortcut.
Unregister {
shortcut: String,
callback: String,
error: String,
},
}
impl Cmd {
pub async fn run<D: crate::ApplicationDispatcherExt + 'static>(
self,
webview_manager: &crate::WebviewManager<D>,
) -> crate::Result<()> {
#[cfg(not(global_shortcut))]
super::allowlist_error(webview_manager, error, "globalShortcut");
#[cfg(global_shortcut)]
match self {
Self::Register {
shortcut,
handler,
callback,
error,
} => {
let dispatcher = webview_manager.current_webview()?.clone();
crate::execute_promise(
webview_manager,
async move {
let mut manager = manager_handle().lock().await;
manager.register_shortcut(shortcut, move || {
let callback_string =
crate::api::rpc::format_callback(handler.to_string(), serde_json::Value::Null);
dispatcher.eval(callback_string.as_str());
})?;
Ok(())
},
callback,
error,
)
.await;
}
Self::Unregister {
shortcut,
callback,
error,
} => {
crate::execute_promise(
webview_manager,
async move {
let mut manager = manager_handle().lock().await;
manager.unregister_shortcut(shortcut)?;
Ok(())
},
callback,
error,
)
.await;
}
}
Ok(())
}
}