feat(global-shortcut): support pressed/released states (#1244)

closes #1243
This commit is contained in:
Amr Bashir
2024-04-24 15:42:01 +02:00
committed by GitHub
parent a9132161df
commit 9c7eb35967
18 changed files with 86 additions and 41 deletions
+1 -1
View File
@@ -24,4 +24,4 @@ log = { workspace = true }
thiserror = { workspace = true }
[target."cfg(not(any(target_os = \"android\", target_os = \"ios\")))".dependencies]
global-hotkey = "0.5"
global-hotkey = { version = "0.5", features = ["serde"]}
+1 -1
View File
@@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_GLOBALSHORTCUT__=function(t){"use strict";function e(t,e,s,n){if("function"==typeof e?t!==e||!n:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return e.get(t)}function s(t,e,s,n,r){if("function"==typeof e?t!==e||!r:!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return e.set(t,s),s}var n,r,i;"function"==typeof SuppressedError&&SuppressedError;class o{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,n.set(this,(()=>{})),r.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,r)){s(this,r,o+1),e(this,n).call(this,t);const a=Object.keys(e(this,i));if(a.length>0){let t=o+1;for(const s of a.sort()){if(parseInt(s)!==t)break;{const r=e(this,i)[s];delete e(this,i)[s],e(this,n).call(this,r),t+=1}}}}else e(this,i)[o.toString()]=t}))}set onmessage(t){s(this,n,t)}get onmessage(){return e(this,n)}toJSON(){return`__CHANNEL__:${this.id}`}}async function a(t,e={},s){return window.__TAURI_INTERNALS__.invoke(t,e,s)}return n=new WeakMap,r=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 s=new o;s.onmessage=e,await a("plugin:global-shortcut|register",{shortcut:t,handler:s})},t.registerAll=async function(t,e){const s=new o;s.onmessage=e,await a("plugin:global-shortcut|register_all",{shortcuts:t,handler:s})},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("m"===s)throw new TypeError("Private method is not writable");if("a"===s&&!n)throw new TypeError("Private accessor was defined without a setter");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"a"===s?n.call(t,r):n?n.value=r: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,"f"),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}}}}else e(this,i,"f")[o.toString()]=t}))}set onmessage(t){r(this,s,t,"f")}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__})}
+9 -3
View File
@@ -10,7 +10,13 @@
import { invoke, Channel } from "@tauri-apps/api/core";
export type ShortcutHandler = (shortcut: string) => void;
export interface ShortcutEvent {
shortcut: string;
id: number;
state: "Released" | "Pressed";
}
export type ShortcutHandler = (event: ShortcutEvent) => void;
/**
* Register a global shortcut.
@@ -31,7 +37,7 @@ async function register(
shortcut: string,
handler: ShortcutHandler,
): Promise<void> {
const h = new Channel<string>();
const h = new Channel<ShortcutEvent>();
h.onmessage = handler;
await invoke("plugin:global-shortcut|register", {
@@ -59,7 +65,7 @@ async function registerAll(
shortcuts: string[],
handler: ShortcutHandler,
): Promise<void> {
const h = new Channel<string>();
const h = new Channel<ShortcutEvent>();
h.onmessage = handler;
await invoke("plugin:global-shortcut|register_all", {
+44 -20
View File
@@ -20,8 +20,12 @@ use std::{
sync::{Arc, Mutex},
};
pub use global_hotkey::hotkey::{Code, HotKey as Shortcut, Modifiers};
pub use global_hotkey::{
hotkey::{Code, HotKey as Shortcut, Modifiers},
GlobalHotKeyEvent as ShortcutEvent, HotKeyState as ShortcutState,
};
use global_hotkey::{GlobalHotKeyEvent, GlobalHotKeyManager};
use serde::Serialize;
use tauri::{
ipc::Channel,
plugin::{Builder as PluginBuilder, TauriPlugin},
@@ -32,8 +36,9 @@ mod error;
pub use error::Error;
type Result<T> = std::result::Result<T, Error>;
type HotKeyId = u32;
type HandlerFn<R> = Box<dyn Fn(&AppHandle<R>, &Shortcut) + Send + Sync + 'static>;
type HandlerFn<R> = Box<dyn Fn(&AppHandle<R>, &Shortcut, ShortcutEvent) + Send + Sync + 'static>;
pub struct ShortcutWrapper(Shortcut);
@@ -63,7 +68,7 @@ pub struct GlobalShortcut<R: Runtime> {
}
impl<R: Runtime> GlobalShortcut<R> {
fn register_internal<F: Fn(&AppHandle<R>, &Shortcut) + Send + Sync + 'static>(
fn register_internal<F: Fn(&AppHandle<R>, &Shortcut, ShortcutEvent) + Send + Sync + 'static>(
&self,
shortcut: Shortcut,
handler: Option<F>,
@@ -82,7 +87,7 @@ impl<R: Runtime> GlobalShortcut<R> {
fn register_all_internal<S, F>(&self, shortcuts: S, handler: Option<F>) -> Result<()>
where
S: IntoIterator<Item = Shortcut>,
F: Fn(&AppHandle<R>, &Shortcut) + Send + Sync + 'static,
F: Fn(&AppHandle<R>, &Shortcut, ShortcutEvent) + Send + Sync + 'static,
{
let handler = handler.map(|h| Arc::new(Box::new(h) as HandlerFn<R>));
@@ -111,7 +116,7 @@ impl<R: Runtime> GlobalShortcut<R> {
{
self.register_internal(
try_into_shortcut(shortcut)?,
None::<fn(&AppHandle<R>, &Shortcut)>,
None::<fn(&AppHandle<R>, &Shortcut, ShortcutEvent)>,
)
}
@@ -120,7 +125,7 @@ impl<R: Runtime> GlobalShortcut<R> {
where
S: TryInto<ShortcutWrapper>,
S::Error: std::error::Error,
F: Fn(&AppHandle<R>, &Shortcut) + Send + Sync + 'static,
F: Fn(&AppHandle<R>, &Shortcut, ShortcutEvent) + Send + Sync + 'static,
{
self.register_internal(try_into_shortcut(shortcut)?, Some(handler))
}
@@ -136,7 +141,7 @@ 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)>)
self.register_all_internal(s, None::<fn(&AppHandle<R>, &Shortcut, ShortcutEvent)>)
}
/// Register multiple shortcuts with a handler.
@@ -145,7 +150,7 @@ impl<R: Runtime> GlobalShortcut<R> {
S: IntoIterator<Item = T>,
T: TryInto<ShortcutWrapper>,
T::Error: std::error::Error,
F: Fn(&AppHandle<R>, &Shortcut) + Send + Sync + 'static,
F: Fn(&AppHandle<R>, &Shortcut, ShortcutEvent) + Send + Sync + 'static,
{
let mut s = Vec::new();
for shortcut in shortcuts {
@@ -225,6 +230,13 @@ where
.map_err(|e| Error::GlobalHotkey(e.to_string()))
}
#[derive(Serialize)]
struct ShortcutJsEvent {
shortcut: String,
id: u32,
state: ShortcutState,
}
#[tauri::command]
fn register<R: Runtime>(
_window: Window<R>,
@@ -233,10 +245,17 @@ fn register<R: Runtime>(
handler: Channel,
) -> Result<()> {
global_shortcut.register_internal(
parse_shortcut(&shortcut)?,
Some(move |_app: &AppHandle<R>, _shortcut: &Shortcut| {
let _ = handler.send(&shortcut);
}),
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);
},
),
)
}
@@ -258,11 +277,16 @@ fn register_all<R: Runtime>(
global_shortcut.register_all_internal(
hotkeys,
Some(move |_app: &AppHandle<R>, shortcut: &Shortcut| {
if let Some(shortcut_str) = shortcut_map.get(&shortcut.id()) {
let _ = handler.send(shortcut_str);
}
}),
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);
},
),
)
}
@@ -341,7 +365,7 @@ impl<R: Runtime> Builder<R> {
}
/// Specify a global shortcut handler that will be triggered for any and all shortcuts.
pub fn with_handler<F: Fn(&AppHandle<R>, &Shortcut) + Send + Sync + 'static>(
pub fn with_handler<F: Fn(&AppHandle<R>, &Shortcut, ShortcutEvent) + Send + Sync + 'static>(
mut self,
handler: F,
) -> Self {
@@ -381,10 +405,10 @@ impl<R: Runtime> Builder<R> {
GlobalHotKeyEvent::set_event_handler(Some(move |e: GlobalHotKeyEvent| {
if let Some(shortcut) = shortcuts_.lock().unwrap().get(&e.id) {
if let Some(handler) = &shortcut.handler {
handler(&app_handle, &shortcut.shortcut);
handler(&app_handle, &shortcut.shortcut, e);
}
if let Some(handler) = &handler {
handler(&app_handle, &shortcut.shortcut);
handler(&app_handle, &shortcut.shortcut, e);
}
}
}));