#[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, }; #[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), } pub(crate) struct ShortcutRegistry { manager: GlobalHotKeyManager, shortcuts: RefCell>, } struct ShortcutInner { #[allow(unused)] shortcut: HotKey, callbacks: Slab>, } 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)(); } } } pub(crate) fn add_shortcut( &self, hotkey: HotKey, callback: Box, ) -> Result { 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), } } }