refactor(global-shortcut): enhance `un/register` to accept an array, remove `un/registerAll` (#1117)

* refactor(shell): enhance `un/register` to accept an array, remove `un/registerAll`

closes #1101

* Update lib.rs

* remove permissions, cleanup docs

* bring back unregister_all

* fmt

* fix build

* bundle

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
pull/1527/head
Amr Bashir 1 year ago committed by GitHub
parent a66549329c
commit 381a466db3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,10 @@
---
"global-shortcut": "patch"
---
Refactored the Rust APIs:
- Renamed `GlobalShortcut::on_all_shortcuts` to `GlobalShortcut::on_shortcuts`
- Renamed `GlobalShortcut::register_all` to `GlobalShortcut::register_multiple`
- Changed `GlobalShortcut::unregister_all` behavior to remove all registerd shortcuts.
- Added `GlobalShortcut::unregister_multiple` to register a list of shortcuts (old behavior of `unregister_all`).

@ -0,0 +1,8 @@
---
"global-shortcut-js": "patch"
---
Refactored the JS APIs:
- Enhanced `register` and `unregister` to take either a single shortcut or an array.
- Removed `registerAll` instead use `register` with an array.

@ -4850,13 +4850,6 @@
"global-shortcut:allow-register"
]
},
{
"description": "global-shortcut:allow-register-all -> Enables the register_all command without any pre-configured scope.",
"type": "string",
"enum": [
"global-shortcut:allow-register-all"
]
},
{
"description": "global-shortcut:allow-unregister -> Enables the unregister command without any pre-configured scope.",
"type": "string",
@ -4885,13 +4878,6 @@
"global-shortcut:deny-register"
]
},
{
"description": "global-shortcut:deny-register-all -> Denies the register_all command without any pre-configured scope.",
"type": "string",
"enum": [
"global-shortcut:deny-register-all"
]
},
{
"description": "global-shortcut:deny-unregister -> Denies the unregister command without any pre-configured scope.",
"type": "string",

@ -1,9 +1,8 @@
<script>
import { writable } from "svelte/store";
import { writable, get } from "svelte/store";
import {
register as registerShortcut,
unregister as unregisterShortcut,
unregisterAll as unregisterAllShortcuts,
} from "@tauri-apps/plugin-global-shortcut";
export let onMessage;
@ -35,7 +34,7 @@
}
function unregisterAll() {
unregisterAllShortcuts()
unregisterShortcut(get(shortcuts))
.then(() => {
shortcuts.update(() => []);
onMessage(`Unregistered all shortcuts`);

@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_GLOBALSHORTCUT__=function(t){"use strict";function e(t,e,r,s){if("a"===r&&!s)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof e?t!==e||!s:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?s:"a"===r?s.call(t):s?s.value:e.get(t)}function r(t,e,r,s,n){if("function"==typeof e?t!==e||!n:!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return e.set(t,r),r}var s,n,i;"function"==typeof SuppressedError&&SuppressedError;class o{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,s.set(this,(()=>{})),n.set(this,0),i.set(this,{}),this.id=function(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}((({message:t,id:o})=>{if(o===e(this,n,"f")){r(this,n,o+1),e(this,s,"f").call(this,t);const a=Object.keys(e(this,i,"f"));if(a.length>0){let t=o+1;for(const r of a.sort()){if(parseInt(r)!==t)break;{const n=e(this,i,"f")[r];delete e(this,i,"f")[r],e(this,s,"f").call(this,n),t+=1}}r(this,n,t)}}else e(this,i,"f")[o.toString()]=t}))}set onmessage(t){r(this,s,t)}get onmessage(){return e(this,s,"f")}toJSON(){return`__CHANNEL__:${this.id}`}}async function a(t,e={},r){return window.__TAURI_INTERNALS__.invoke(t,e,r)}return s=new WeakMap,n=new WeakMap,i=new WeakMap,t.isRegistered=async function(t){return await a("plugin:global-shortcut|is_registered",{shortcut:t})},t.register=async function(t,e){const r=new o;r.onmessage=e,await a("plugin:global-shortcut|register",{shortcut:t,handler:r})},t.registerAll=async function(t,e){const r=new o;r.onmessage=e,await a("plugin:global-shortcut|register_all",{shortcuts:t,handler:r})},t.unregister=async function(t){await a("plugin:global-shortcut|unregister",{shortcut:t})},t.unregisterAll=async function(){await a("plugin:global-shortcut|unregister_all")},t}({});Object.defineProperty(window.__TAURI__,"globalShortcut",{value:__TAURI_PLUGIN_GLOBALSHORTCUT__})}
if("__TAURI__"in window){var __TAURI_PLUGIN_GLOBALSHORTCUT__=function(t){"use strict";function e(t,e,r,s){if("a"===r&&!s)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof e?t!==e||!s:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?s:"a"===r?s.call(t):s?s.value:e.get(t)}function r(t,e,r,s,n){if("function"==typeof e?t!==e||!n:!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return e.set(t,r),r}var s,n,i;"function"==typeof SuppressedError&&SuppressedError;class o{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,s.set(this,(()=>{})),n.set(this,0),i.set(this,{}),this.id=function(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}((({message:t,id:o})=>{if(o===e(this,n,"f")){r(this,n,o+1),e(this,s,"f").call(this,t);const a=Object.keys(e(this,i,"f"));if(a.length>0){let t=o+1;for(const r of a.sort()){if(parseInt(r)!==t)break;{const n=e(this,i,"f")[r];delete e(this,i,"f")[r],e(this,s,"f").call(this,n),t+=1}}r(this,n,t)}}else e(this,i,"f")[o.toString()]=t}))}set onmessage(t){r(this,s,t)}get onmessage(){return e(this,s,"f")}toJSON(){return`__CHANNEL__:${this.id}`}}async function a(t,e={},r){return window.__TAURI_INTERNALS__.invoke(t,e,r)}return s=new WeakMap,n=new WeakMap,i=new WeakMap,t.isRegistered=async function(t){return await a("plugin:global-shortcut|is_registered",{shortcut:t})},t.register=async function(t,e){const r=new o;return r.onmessage=e,await a("plugin:global-shortcut|register",{shortcuts:Array.isArray(t)?t:[t],handler:r})},t.unregister=async function(t){return await a("plugin:global-shortcut|unregister",{shortcuts:Array.isArray(t)?t:[t]})},t.unregisterAll=async function(){return await a("plugin:global-shortcut|unregister_all",{})},t}({});Object.defineProperty(window.__TAURI__,"globalShortcut",{value:__TAURI_PLUGIN_GLOBALSHORTCUT__})}

@ -2,13 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
const COMMANDS: &[&str] = &[
"register",
"register_all",
"unregister",
"unregister_all",
"is_registered",
];
const COMMANDS: &[&str] = &["register", "unregister", "unregister_all", "is_registered"];
fn main() {
tauri_plugin::Builder::new(COMMANDS)

@ -19,15 +19,28 @@ export interface ShortcutEvent {
export type ShortcutHandler = (event: ShortcutEvent) => void;
/**
* Register a global shortcut.
* Register a global shortcut or a list of shortcuts.
*
* The handler is called when any of the registered shortcuts are pressed by the user.
*
* If the shortcut is already taken by another application, the handler will not be triggered.
* Make sure the shortcut is as unique as possible while still taking user experience into consideration.
*
* @example
* ```typescript
* import { register } from '@tauri-apps/plugin-global-shortcut';
*
* // register a single hotkey
* await register('CommandOrControl+Shift+C', (event) => {
* if (event.state === "Pressed") {
* console.log('Shortcut triggered');
* }
* });
*
* // or register multiple hotkeys at once
* await register(['CommandOrControl+Shift+C', 'Alt+A'], (event) => {
* console.log(`Shortcut ${event.shortcut} triggered`);
* });
* ```
*
* @param shortcut Shortcut definition, modifiers and key separated by "+" e.g. CmdOrControl+Q
@ -36,97 +49,75 @@ export type ShortcutHandler = (event: ShortcutEvent) => void;
* @since 2.0.0
*/
async function register(
shortcut: string,
shortcuts: string | string[],
handler: ShortcutHandler,
): Promise<void> {
const h = new Channel<ShortcutEvent>();
h.onmessage = handler;
await invoke("plugin:global-shortcut|register", {
shortcut,
return await invoke("plugin:global-shortcut|register", {
shortcuts: Array.isArray(shortcuts) ? shortcuts : [shortcuts],
handler: h,
});
}
/**
* Register a collection of global shortcuts.
* Unregister a global shortcut or a list of shortcuts.
*
* @example
* ```typescript
* import { registerAll } from '@tauri-apps/plugin-global-shortcut';
* await registerAll(['CommandOrControl+Shift+C', 'Ctrl+Alt+F12'], (event) => {
* console.log(`Shortcut ${event.shortcut} ${event.state}`);
* });
* import { unregister } from '@tauri-apps/plugin-global-shortcut';
*
* // unregister a single hotkey
* await unregister('CmdOrControl+Space');
*
* // or unregister multiple hotkeys at the same time
* await unregister(['CmdOrControl+Space', 'Alt+A']);
* ```
*
* @param shortcuts Array of shortcut definitions, modifiers and key separated by "+" e.g. CmdOrControl+Q
* @param handler Shortcut handler callback - takes the triggered shortcut as argument
* @param shortcut shortcut definition (modifiers and key separated by "+" e.g. CmdOrControl+Q), also accepts a list of shortcuts
*
* @since 2.0.0
*/
async function registerAll(
shortcuts: string[],
handler: ShortcutHandler,
): Promise<void> {
const h = new Channel<ShortcutEvent>();
h.onmessage = handler;
await invoke("plugin:global-shortcut|register_all", {
shortcuts,
handler: h,
async function unregister(shortcuts: string | string[]): Promise<void> {
return await invoke("plugin:global-shortcut|unregister", {
shortcuts: Array.isArray(shortcuts) ? shortcuts : [shortcuts],
});
}
/**
* Determines whether the given shortcut is registered by this application or not.
*
* If the shortcut is registered by another application, it will still return `false`.
* Unregister all global shortcuts.
*
* @example
* ```typescript
* import { isRegistered } from '@tauri-apps/plugin-global-shortcut';
* const isRegistered = await isRegistered('CommandOrControl+P');
* import { unregisterAll } from '@tauri-apps/plugin-global-shortcut';
* await unregisterAll();
* ```
*
* @param shortcut shortcut definition, modifiers and key separated by "+" e.g. CmdOrControl+Q
*
* @since 2.0.0
*/
async function isRegistered(shortcut: string): Promise<boolean> {
return await invoke("plugin:global-shortcut|is_registered", {
shortcut,
});
async function unregisterAll(): Promise<void> {
return await invoke("plugin:global-shortcut|unregister_all", {});
}
/**
* Unregister a global shortcut.
* Determines whether the given shortcut is registered by this application or not.
*
* If the shortcut is registered by another application, it will still return `false`.
*
* @example
* ```typescript
* import { unregister } from '@tauri-apps/plugin-global-shortcut';
* await unregister('CmdOrControl+Space');
* import { isRegistered } from '@tauri-apps/plugin-global-shortcut';
* const isRegistered = await isRegistered('CommandOrControl+P');
* ```
*
* @param shortcut shortcut definition, modifiers and key separated by "+" e.g. CmdOrControl+Q
*
* @since 2.0.0
*/
async function unregister(shortcut: string): Promise<void> {
await invoke("plugin:global-shortcut|unregister", {
async function isRegistered(shortcut: string): Promise<boolean> {
return await invoke("plugin:global-shortcut|is_registered", {
shortcut,
});
}
/**
* Unregisters all shortcuts registered by the application.
* @example
* ```typescript
* import { unregisterAll } from '@tauri-apps/plugin-global-shortcut';
* await unregisterAll();
* ```
*
* @since 2.0.0
*/
async function unregisterAll(): Promise<void> {
await invoke("plugin:global-shortcut|unregister_all");
}
export { register, registerAll, isRegistered, unregister, unregisterAll };
export { register, unregister, unregisterAll, isRegistered };

@ -2,7 +2,7 @@
[default]
description = """
No features are enabled by default, as we believe
the shortcuts can be inherently dangerous and it is
the shortcuts can be inherently dangerous and it is
application specific if specific shortcuts should be
registered or unregistered.
"""

@ -29,7 +29,7 @@ use serde::Serialize;
use tauri::{
ipc::Channel,
plugin::{Builder as PluginBuilder, TauriPlugin},
AppHandle, Manager, Runtime, State, Window,
AppHandle, Manager, Runtime, State,
};
mod error;
@ -84,7 +84,7 @@ impl<R: Runtime> GlobalShortcut<R> {
Ok(())
}
fn register_all_internal<S, F>(&self, shortcuts: S, handler: Option<F>) -> Result<()>
fn register_multiple_internal<S, F>(&self, shortcuts: S, handler: Option<F>) -> Result<()>
where
S: IntoIterator<Item = Shortcut>,
F: Fn(&AppHandle<R>, &Shortcut, ShortcutEvent) + Send + Sync + 'static,
@ -107,7 +107,9 @@ impl<R: Runtime> GlobalShortcut<R> {
Ok(())
}
}
impl<R: Runtime> GlobalShortcut<R> {
/// Register a shortcut.
pub fn register<S>(&self, shortcut: S) -> Result<()>
where
@ -131,7 +133,7 @@ impl<R: Runtime> GlobalShortcut<R> {
}
/// Register multiple shortcuts.
pub fn register_all<S, T>(&self, shortcuts: S) -> Result<()>
pub fn register_multiple<S, T>(&self, shortcuts: S) -> Result<()>
where
S: IntoIterator<Item = T>,
T: TryInto<ShortcutWrapper>,
@ -141,11 +143,11 @@ impl<R: Runtime> GlobalShortcut<R> {
for shortcut in shortcuts {
s.push(try_into_shortcut(shortcut)?);
}
self.register_all_internal(s, None::<fn(&AppHandle<R>, &Shortcut, ShortcutEvent)>)
self.register_multiple_internal(s, None::<fn(&AppHandle<R>, &Shortcut, ShortcutEvent)>)
}
/// Register multiple shortcuts with a handler.
pub fn on_all_shortcuts<S, T, F>(&self, shortcuts: S, handler: F) -> Result<()>
pub fn on_shortcuts<S, T, F>(&self, shortcuts: S, handler: F) -> Result<()>
where
S: IntoIterator<Item = T>,
T: TryInto<ShortcutWrapper>,
@ -156,9 +158,10 @@ impl<R: Runtime> GlobalShortcut<R> {
for shortcut in shortcuts {
s.push(try_into_shortcut(shortcut)?);
}
self.register_all_internal(s, Some(handler))
self.register_multiple_internal(s, Some(handler))
}
/// Unregister a shortcut
pub fn unregister<S: TryInto<ShortcutWrapper>>(&self, shortcut: S) -> Result<()>
where
S::Error: std::error::Error,
@ -169,7 +172,8 @@ impl<R: Runtime> GlobalShortcut<R> {
Ok(())
}
pub fn unregister_all<T: TryInto<ShortcutWrapper>, S: IntoIterator<Item = T>>(
/// Unregister multiple shortcuts.
pub fn unregister_multiple<T: TryInto<ShortcutWrapper>, S: IntoIterator<Item = T>>(
&self,
shortcuts: S,
) -> Result<()>
@ -191,6 +195,16 @@ impl<R: Runtime> GlobalShortcut<R> {
Ok(())
}
/// Unregister all registered shortcuts.
pub fn unregister_all(&self) -> Result<()> {
let mut shortcuts = self.shortcuts.lock().unwrap();
let hotkeys = std::mem::take(&mut *shortcuts);
let hotkeys = hotkeys.values().map(|s| s.shortcut).collect::<Vec<_>>();
self.manager
.unregister_all(hotkeys.as_slice())
.map_err(Into::into)
}
/// Determines whether the given shortcut is registered by this application or not.
///
/// If the shortcut is registered by another application, it will still return `false`.
@ -239,29 +253,7 @@ struct ShortcutJsEvent {
#[tauri::command]
fn register<R: Runtime>(
_window: Window<R>,
global_shortcut: State<'_, GlobalShortcut<R>>,
shortcut: String,
handler: Channel,
) -> Result<()> {
global_shortcut.register_internal(
parse_shortcut(shortcut)?,
Some(
move |_app: &AppHandle<R>, shortcut: &Shortcut, e: ShortcutEvent| {
let js_event = ShortcutJsEvent {
id: e.id,
state: e.state,
shortcut: shortcut.into_string(),
};
let _ = handler.send(js_event);
},
),
)
}
#[tauri::command]
fn register_all<R: Runtime>(
_window: Window<R>,
_app: AppHandle<R>,
global_shortcut: State<'_, GlobalShortcut<R>>,
shortcuts: Vec<String>,
handler: Channel,
@ -275,7 +267,7 @@ fn register_all<R: Runtime>(
hotkeys.push(hotkey);
}
global_shortcut.register_all_internal(
global_shortcut.register_multiple_internal(
hotkeys,
Some(
move |_app: &AppHandle<R>, shortcut: &Shortcut, e: ShortcutEvent| {
@ -294,22 +286,21 @@ fn register_all<R: Runtime>(
fn unregister<R: Runtime>(
_app: AppHandle<R>,
global_shortcut: State<'_, GlobalShortcut<R>>,
shortcut: String,
shortcuts: Vec<String>,
) -> Result<()> {
global_shortcut.unregister(parse_shortcut(shortcut)?)
let mut hotkeys = Vec::new();
for shortcut in shortcuts {
hotkeys.push(parse_shortcut(&shortcut)?);
}
global_shortcut.unregister_multiple(hotkeys)
}
#[tauri::command]
fn unregister_all<R: Runtime>(
_app: AppHandle<R>,
global_shortcut: State<'_, GlobalShortcut<R>>,
shortcuts: Vec<String>,
) -> Result<()> {
let mut hotkeys = Vec::new();
for shortcut in shortcuts {
hotkeys.push(parse_shortcut(&shortcut)?);
}
global_shortcut.unregister_all(hotkeys)
global_shortcut.unregister_all()
}
#[tauri::command]
@ -379,10 +370,9 @@ impl<R: Runtime> Builder<R> {
PluginBuilder::new("global-shortcut")
.invoke_handler(tauri::generate_handler![
register,
register_all,
unregister,
unregister_all,
is_registered
is_registered,
])
.setup(move |app, _api| {
let manager = GlobalHotKeyManager::new()?;

Loading…
Cancel
Save