#[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;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ShortcutHandle {
id: u32,
number: usize,
}
impl ShortcutHandle {
pub fn remove(&self) {
window().remove_shortcut(*self);
}
}
#[non_exhaustive]
#[derive(Debug, Clone)]
pub enum ShortcutRegistryError {
InvalidShortcut(String),
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()>>,
}
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<dyn FnMut()>,
) -> 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),
}
}
}