* Only react to hotkeys that are pressed The global shortcuts are otherwise also triggering when the hotkey is being released. This affects at least Windows. * Provide `HotkeyState` on Global Shortcut Events Instead of ignoring the events where the shortcut is released, the state is now provided as an argument to the event handler.
304 lines
11 KiB
Rust
304 lines
11 KiB
Rust
#[cfg(any(
|
|
target_os = "windows",
|
|
target_os = "macos",
|
|
target_os = "linux",
|
|
target_os = "dragonfly",
|
|
target_os = "freebsd",
|
|
target_os = "netbsd",
|
|
target_os = "openbsd"
|
|
))]
|
|
pub use global_hotkey::{
|
|
hotkey::{Code, HotKey},
|
|
Error as HotkeyError, GlobalHotKeyEvent, GlobalHotKeyManager, HotKeyState,
|
|
};
|
|
|
|
#[cfg(any(target_os = "ios", target_os = "android"))]
|
|
pub use crate::mobile_shortcut::*;
|
|
|
|
use crate::window;
|
|
use dioxus_html::input_data::keyboard_types::Modifiers;
|
|
use slab::Slab;
|
|
use std::{cell::RefCell, collections::HashMap, rc::Rc, str::FromStr};
|
|
use tao::keyboard::ModifiersState;
|
|
|
|
/// An global id for a shortcut.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub struct ShortcutHandle {
|
|
id: u32,
|
|
number: usize,
|
|
}
|
|
|
|
impl ShortcutHandle {
|
|
/// Remove the shortcut.
|
|
pub fn remove(&self) {
|
|
window().remove_shortcut(*self);
|
|
}
|
|
}
|
|
|
|
/// An error that can occur when registering a shortcut.
|
|
#[non_exhaustive]
|
|
#[derive(Debug, Clone)]
|
|
pub enum ShortcutRegistryError {
|
|
/// The shortcut is invalid.
|
|
InvalidShortcut(String),
|
|
/// An unknown error occurred.
|
|
Other(Rc<dyn std::error::Error>),
|
|
}
|
|
|
|
pub(crate) struct ShortcutRegistry {
|
|
manager: GlobalHotKeyManager,
|
|
shortcuts: RefCell<HashMap<u32, ShortcutInner>>,
|
|
}
|
|
|
|
struct ShortcutInner {
|
|
#[allow(unused)]
|
|
shortcut: HotKey,
|
|
callbacks: Slab<Box<dyn FnMut(HotKeyState)>>,
|
|
}
|
|
|
|
impl ShortcutRegistry {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
manager: GlobalHotKeyManager::new().unwrap(),
|
|
shortcuts: RefCell::new(HashMap::new()),
|
|
}
|
|
}
|
|
|
|
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
|
pub(crate) fn call_handlers(&self, id: GlobalHotKeyEvent) {
|
|
if let Some(ShortcutInner { callbacks, .. }) = self.shortcuts.borrow_mut().get_mut(&id.id) {
|
|
for (_, callback) in callbacks.iter_mut() {
|
|
(callback)(id.state);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn add_shortcut(
|
|
&self,
|
|
hotkey: HotKey,
|
|
callback: Box<dyn FnMut(HotKeyState)>,
|
|
) -> Result<ShortcutHandle, ShortcutRegistryError> {
|
|
let accelerator_id = hotkey.clone().id();
|
|
|
|
let mut shortcuts = self.shortcuts.borrow_mut();
|
|
|
|
if let Some(callbacks) = shortcuts.get_mut(&accelerator_id) {
|
|
return Ok(ShortcutHandle {
|
|
id: accelerator_id,
|
|
number: callbacks.callbacks.insert(callback),
|
|
});
|
|
};
|
|
|
|
self.manager.register(hotkey).map_err(|e| match e {
|
|
HotkeyError::HotKeyParseError(shortcut) => {
|
|
ShortcutRegistryError::InvalidShortcut(shortcut)
|
|
}
|
|
err => ShortcutRegistryError::Other(Rc::new(err)),
|
|
})?;
|
|
|
|
let mut shortcut = ShortcutInner {
|
|
shortcut: hotkey,
|
|
callbacks: Slab::new(),
|
|
};
|
|
|
|
let id = shortcut.callbacks.insert(callback);
|
|
|
|
shortcuts.insert(accelerator_id, shortcut);
|
|
|
|
Ok(ShortcutHandle {
|
|
id: accelerator_id,
|
|
number: id,
|
|
})
|
|
}
|
|
|
|
pub(crate) fn remove_shortcut(&self, id: ShortcutHandle) {
|
|
let mut shortcuts = self.shortcuts.borrow_mut();
|
|
if let Some(callbacks) = shortcuts.get_mut(&id.id) {
|
|
let _ = callbacks.callbacks.remove(id.number);
|
|
if callbacks.callbacks.is_empty() {
|
|
if let Some(_shortcut) = shortcuts.remove(&id.id) {
|
|
let _ = self.manager.unregister(_shortcut.shortcut);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn remove_all(&self) {
|
|
let mut shortcuts = self.shortcuts.borrow_mut();
|
|
let hotkeys: Vec<_> = shortcuts.drain().map(|(_, v)| v.shortcut).collect();
|
|
let _ = self.manager.unregister_all(&hotkeys);
|
|
}
|
|
}
|
|
|
|
pub trait IntoAccelerator {
|
|
fn accelerator(&self) -> HotKey;
|
|
}
|
|
|
|
impl IntoAccelerator for (dioxus_html::KeyCode, ModifiersState) {
|
|
fn accelerator(&self) -> HotKey {
|
|
HotKey::new(Some(self.1.into_modifiers_state()), self.0.into_key_code())
|
|
}
|
|
}
|
|
|
|
impl IntoAccelerator for (ModifiersState, dioxus_html::KeyCode) {
|
|
fn accelerator(&self) -> HotKey {
|
|
HotKey::new(Some(self.0.into_modifiers_state()), self.1.into_key_code())
|
|
}
|
|
}
|
|
|
|
impl IntoAccelerator for dioxus_html::KeyCode {
|
|
fn accelerator(&self) -> HotKey {
|
|
HotKey::new(None, self.into_key_code())
|
|
}
|
|
}
|
|
|
|
impl IntoAccelerator for &str {
|
|
fn accelerator(&self) -> HotKey {
|
|
HotKey::from_str(self).unwrap()
|
|
}
|
|
}
|
|
|
|
pub trait IntoModifiersState {
|
|
fn into_modifiers_state(self) -> Modifiers;
|
|
}
|
|
|
|
impl IntoModifiersState for ModifiersState {
|
|
fn into_modifiers_state(self) -> Modifiers {
|
|
let mut modifiers = Modifiers::default();
|
|
if self.shift_key() {
|
|
modifiers |= Modifiers::SHIFT;
|
|
}
|
|
if self.control_key() {
|
|
modifiers |= Modifiers::CONTROL;
|
|
}
|
|
if self.alt_key() {
|
|
modifiers |= Modifiers::ALT;
|
|
}
|
|
if self.super_key() {
|
|
modifiers |= Modifiers::META;
|
|
}
|
|
|
|
modifiers
|
|
}
|
|
}
|
|
|
|
impl IntoModifiersState for Modifiers {
|
|
fn into_modifiers_state(self) -> Modifiers {
|
|
self
|
|
}
|
|
}
|
|
|
|
pub trait IntoKeyCode {
|
|
fn into_key_code(self) -> Code;
|
|
}
|
|
|
|
impl IntoKeyCode for Code {
|
|
fn into_key_code(self) -> Code {
|
|
self
|
|
}
|
|
}
|
|
|
|
impl IntoKeyCode for dioxus_html::KeyCode {
|
|
fn into_key_code(self) -> Code {
|
|
match self {
|
|
dioxus_html::KeyCode::Backspace => Code::Backspace,
|
|
dioxus_html::KeyCode::Tab => Code::Tab,
|
|
dioxus_html::KeyCode::Clear => Code::NumpadClear,
|
|
dioxus_html::KeyCode::Enter => Code::Enter,
|
|
dioxus_html::KeyCode::Shift => Code::ShiftLeft,
|
|
dioxus_html::KeyCode::Ctrl => Code::ControlLeft,
|
|
dioxus_html::KeyCode::Alt => Code::AltLeft,
|
|
dioxus_html::KeyCode::Pause => Code::Pause,
|
|
dioxus_html::KeyCode::CapsLock => Code::CapsLock,
|
|
dioxus_html::KeyCode::Escape => Code::Escape,
|
|
dioxus_html::KeyCode::Space => Code::Space,
|
|
dioxus_html::KeyCode::PageUp => Code::PageUp,
|
|
dioxus_html::KeyCode::PageDown => Code::PageDown,
|
|
dioxus_html::KeyCode::End => Code::End,
|
|
dioxus_html::KeyCode::Home => Code::Home,
|
|
dioxus_html::KeyCode::LeftArrow => Code::ArrowLeft,
|
|
dioxus_html::KeyCode::UpArrow => Code::ArrowUp,
|
|
dioxus_html::KeyCode::RightArrow => Code::ArrowRight,
|
|
dioxus_html::KeyCode::DownArrow => Code::ArrowDown,
|
|
dioxus_html::KeyCode::Insert => Code::Insert,
|
|
dioxus_html::KeyCode::Delete => Code::Delete,
|
|
dioxus_html::KeyCode::Num0 => Code::Numpad0,
|
|
dioxus_html::KeyCode::Num1 => Code::Numpad1,
|
|
dioxus_html::KeyCode::Num2 => Code::Numpad2,
|
|
dioxus_html::KeyCode::Num3 => Code::Numpad3,
|
|
dioxus_html::KeyCode::Num4 => Code::Numpad4,
|
|
dioxus_html::KeyCode::Num5 => Code::Numpad5,
|
|
dioxus_html::KeyCode::Num6 => Code::Numpad6,
|
|
dioxus_html::KeyCode::Num7 => Code::Numpad7,
|
|
dioxus_html::KeyCode::Num8 => Code::Numpad8,
|
|
dioxus_html::KeyCode::Num9 => Code::Numpad9,
|
|
dioxus_html::KeyCode::A => Code::KeyA,
|
|
dioxus_html::KeyCode::B => Code::KeyB,
|
|
dioxus_html::KeyCode::C => Code::KeyC,
|
|
dioxus_html::KeyCode::D => Code::KeyD,
|
|
dioxus_html::KeyCode::E => Code::KeyE,
|
|
dioxus_html::KeyCode::F => Code::KeyF,
|
|
dioxus_html::KeyCode::G => Code::KeyG,
|
|
dioxus_html::KeyCode::H => Code::KeyH,
|
|
dioxus_html::KeyCode::I => Code::KeyI,
|
|
dioxus_html::KeyCode::J => Code::KeyJ,
|
|
dioxus_html::KeyCode::K => Code::KeyK,
|
|
dioxus_html::KeyCode::L => Code::KeyL,
|
|
dioxus_html::KeyCode::M => Code::KeyM,
|
|
dioxus_html::KeyCode::N => Code::KeyN,
|
|
dioxus_html::KeyCode::O => Code::KeyO,
|
|
dioxus_html::KeyCode::P => Code::KeyP,
|
|
dioxus_html::KeyCode::Q => Code::KeyQ,
|
|
dioxus_html::KeyCode::R => Code::KeyR,
|
|
dioxus_html::KeyCode::S => Code::KeyS,
|
|
dioxus_html::KeyCode::T => Code::KeyT,
|
|
dioxus_html::KeyCode::U => Code::KeyU,
|
|
dioxus_html::KeyCode::V => Code::KeyV,
|
|
dioxus_html::KeyCode::W => Code::KeyW,
|
|
dioxus_html::KeyCode::X => Code::KeyX,
|
|
dioxus_html::KeyCode::Y => Code::KeyY,
|
|
dioxus_html::KeyCode::Z => Code::KeyZ,
|
|
dioxus_html::KeyCode::Numpad0 => Code::Numpad0,
|
|
dioxus_html::KeyCode::Numpad1 => Code::Numpad1,
|
|
dioxus_html::KeyCode::Numpad2 => Code::Numpad2,
|
|
dioxus_html::KeyCode::Numpad3 => Code::Numpad3,
|
|
dioxus_html::KeyCode::Numpad4 => Code::Numpad4,
|
|
dioxus_html::KeyCode::Numpad5 => Code::Numpad5,
|
|
dioxus_html::KeyCode::Numpad6 => Code::Numpad6,
|
|
dioxus_html::KeyCode::Numpad7 => Code::Numpad7,
|
|
dioxus_html::KeyCode::Numpad8 => Code::Numpad8,
|
|
dioxus_html::KeyCode::Numpad9 => Code::Numpad9,
|
|
dioxus_html::KeyCode::Multiply => Code::NumpadMultiply,
|
|
dioxus_html::KeyCode::Add => Code::NumpadAdd,
|
|
dioxus_html::KeyCode::Subtract => Code::NumpadSubtract,
|
|
dioxus_html::KeyCode::DecimalPoint => Code::NumpadDecimal,
|
|
dioxus_html::KeyCode::Divide => Code::NumpadDivide,
|
|
dioxus_html::KeyCode::F1 => Code::F1,
|
|
dioxus_html::KeyCode::F2 => Code::F2,
|
|
dioxus_html::KeyCode::F3 => Code::F3,
|
|
dioxus_html::KeyCode::F4 => Code::F4,
|
|
dioxus_html::KeyCode::F5 => Code::F5,
|
|
dioxus_html::KeyCode::F6 => Code::F6,
|
|
dioxus_html::KeyCode::F7 => Code::F7,
|
|
dioxus_html::KeyCode::F8 => Code::F8,
|
|
dioxus_html::KeyCode::F9 => Code::F9,
|
|
dioxus_html::KeyCode::F10 => Code::F10,
|
|
dioxus_html::KeyCode::F11 => Code::F11,
|
|
dioxus_html::KeyCode::F12 => Code::F12,
|
|
dioxus_html::KeyCode::NumLock => Code::NumLock,
|
|
dioxus_html::KeyCode::ScrollLock => Code::ScrollLock,
|
|
dioxus_html::KeyCode::Semicolon => Code::Semicolon,
|
|
dioxus_html::KeyCode::EqualSign => Code::Equal,
|
|
dioxus_html::KeyCode::Comma => Code::Comma,
|
|
dioxus_html::KeyCode::Period => Code::Period,
|
|
dioxus_html::KeyCode::ForwardSlash => Code::Slash,
|
|
dioxus_html::KeyCode::GraveAccent => Code::Backquote,
|
|
dioxus_html::KeyCode::OpenBracket => Code::BracketLeft,
|
|
dioxus_html::KeyCode::BackSlash => Code::Backslash,
|
|
dioxus_html::KeyCode::CloseBracket => Code::BracketRight,
|
|
dioxus_html::KeyCode::SingleQuote => Code::Quote,
|
|
key => panic!("Failed to convert {:?} to tao::keyboard::KeyCode, try using tao::keyboard::KeyCode directly", key),
|
|
}
|
|
}
|
|
}
|