shortcut.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. use std::{cell::RefCell, collections::HashMap, rc::Rc};
  2. use dioxus_core::ScopeState;
  3. use dioxus_html::input_data::keyboard_types::Modifiers;
  4. use slab::Slab;
  5. use wry::application::{
  6. accelerator::{Accelerator, AcceleratorId},
  7. event_loop::EventLoopWindowTarget,
  8. global_shortcut::{GlobalShortcut, ShortcutManager, ShortcutManagerError},
  9. keyboard::{KeyCode, ModifiersState},
  10. };
  11. use crate::{use_window, DesktopContext};
  12. #[derive(Clone)]
  13. pub(crate) struct ShortcutRegistry {
  14. manager: Rc<RefCell<ShortcutManager>>,
  15. shortcuts: ShortcutMap,
  16. }
  17. type ShortcutMap = Rc<RefCell<HashMap<AcceleratorId, Shortcut>>>;
  18. struct Shortcut {
  19. shortcut: GlobalShortcut,
  20. callbacks: Slab<Box<dyn FnMut()>>,
  21. }
  22. impl Shortcut {
  23. fn insert(&mut self, callback: Box<dyn FnMut()>) -> usize {
  24. self.callbacks.insert(callback)
  25. }
  26. fn remove(&mut self, id: usize) {
  27. let _ = self.callbacks.remove(id);
  28. }
  29. fn is_empty(&self) -> bool {
  30. self.callbacks.is_empty()
  31. }
  32. }
  33. impl ShortcutRegistry {
  34. pub fn new<T>(target: &EventLoopWindowTarget<T>) -> Self {
  35. let myself = Self {
  36. manager: Rc::new(RefCell::new(ShortcutManager::new(target))),
  37. shortcuts: Rc::new(RefCell::new(HashMap::new())),
  38. };
  39. // prevent CTRL+R from reloading the page which breaks apps
  40. let _ = myself.add_shortcut(
  41. Some(ModifiersState::CONTROL),
  42. KeyCode::KeyR,
  43. Box::new(|| {}),
  44. );
  45. myself
  46. }
  47. pub(crate) fn call_handlers(&self, id: AcceleratorId) {
  48. if let Some(Shortcut { callbacks, .. }) = self.shortcuts.borrow_mut().get_mut(&id) {
  49. for (_, callback) in callbacks.iter_mut() {
  50. (callback)();
  51. }
  52. }
  53. }
  54. pub(crate) fn add_shortcut(
  55. &self,
  56. modifiers: impl Into<Option<ModifiersState>>,
  57. key: KeyCode,
  58. callback: Box<dyn FnMut()>,
  59. ) -> Result<ShortcutId, ShortcutRegistryError> {
  60. let accelerator = Accelerator::new(modifiers, key);
  61. let accelerator_id = accelerator.clone().id();
  62. let mut shortcuts = self.shortcuts.borrow_mut();
  63. Ok(
  64. if let Some(callbacks) = shortcuts.get_mut(&accelerator_id) {
  65. let id = callbacks.insert(callback);
  66. ShortcutId {
  67. id: accelerator_id,
  68. number: id,
  69. }
  70. } else {
  71. match self.manager.borrow_mut().register(accelerator) {
  72. Ok(global_shortcut) => {
  73. let mut slab = Slab::new();
  74. let id = slab.insert(callback);
  75. let shortcut = Shortcut {
  76. shortcut: global_shortcut,
  77. callbacks: slab,
  78. };
  79. shortcuts.insert(accelerator_id, shortcut);
  80. ShortcutId {
  81. id: accelerator_id,
  82. number: id,
  83. }
  84. }
  85. Err(ShortcutManagerError::InvalidAccelerator(shortcut)) => {
  86. return Err(ShortcutRegistryError::InvalidShortcut(shortcut))
  87. }
  88. Err(err) => return Err(ShortcutRegistryError::Other(Box::new(err))),
  89. }
  90. },
  91. )
  92. }
  93. pub(crate) fn remove_shortcut(&self, id: ShortcutId) {
  94. let mut shortcuts = self.shortcuts.borrow_mut();
  95. if let Some(callbacks) = shortcuts.get_mut(&id.id) {
  96. callbacks.remove(id.number);
  97. if callbacks.is_empty() {
  98. if let Some(shortcut) = shortcuts.remove(&id.id) {
  99. let _ = self.manager.borrow_mut().unregister(shortcut.shortcut);
  100. }
  101. }
  102. }
  103. }
  104. pub(crate) fn remove_all(&self) {
  105. let mut shortcuts = self.shortcuts.borrow_mut();
  106. shortcuts.clear();
  107. let _ = self.manager.borrow_mut().unregister_all();
  108. // prevent CTRL+R from reloading the page which breaks apps
  109. let _ = self.add_shortcut(
  110. Some(ModifiersState::CONTROL),
  111. KeyCode::KeyR,
  112. Box::new(|| {}),
  113. );
  114. }
  115. }
  116. #[non_exhaustive]
  117. #[derive(Debug)]
  118. /// An error that can occur when registering a shortcut.
  119. pub enum ShortcutRegistryError {
  120. /// The shortcut is invalid.
  121. InvalidShortcut(String),
  122. /// An unknown error occurred.
  123. Other(Box<dyn std::error::Error>),
  124. }
  125. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
  126. /// An global id for a shortcut.
  127. pub struct ShortcutId {
  128. id: AcceleratorId,
  129. number: usize,
  130. }
  131. /// A global shortcut. This will be automatically removed when it is dropped.
  132. pub struct ShortcutHandle {
  133. desktop: DesktopContext,
  134. /// The id of the shortcut
  135. pub shortcut_id: ShortcutId,
  136. }
  137. /// Get a closure that executes any JavaScript in the WebView context.
  138. pub fn use_global_shortcut(
  139. cx: &ScopeState,
  140. key: impl IntoKeyCode,
  141. modifiers: impl IntoModifersState,
  142. handler: impl FnMut() + 'static,
  143. ) -> &Result<ShortcutHandle, ShortcutRegistryError> {
  144. let desktop = use_window(cx);
  145. cx.use_hook(move || {
  146. let desktop = desktop.clone();
  147. let id = desktop.create_shortcut(
  148. key.into_key_code(),
  149. modifiers.into_modifiers_state(),
  150. handler,
  151. );
  152. Ok(ShortcutHandle {
  153. desktop,
  154. shortcut_id: id?,
  155. })
  156. })
  157. }
  158. impl ShortcutHandle {
  159. /// Remove the shortcut.
  160. pub fn remove(&self) {
  161. self.desktop.remove_shortcut(self.shortcut_id);
  162. }
  163. }
  164. impl Drop for ShortcutHandle {
  165. fn drop(&mut self) {
  166. self.remove()
  167. }
  168. }
  169. pub trait IntoModifersState {
  170. fn into_modifiers_state(self) -> ModifiersState;
  171. }
  172. impl IntoModifersState for ModifiersState {
  173. fn into_modifiers_state(self) -> ModifiersState {
  174. self
  175. }
  176. }
  177. impl IntoModifersState for Modifiers {
  178. fn into_modifiers_state(self) -> ModifiersState {
  179. let mut state = ModifiersState::empty();
  180. if self.contains(Modifiers::SHIFT) {
  181. state |= ModifiersState::SHIFT
  182. }
  183. if self.contains(Modifiers::CONTROL) {
  184. state |= ModifiersState::CONTROL
  185. }
  186. if self.contains(Modifiers::ALT) {
  187. state |= ModifiersState::ALT
  188. }
  189. if self.contains(Modifiers::META) || self.contains(Modifiers::SUPER) {
  190. state |= ModifiersState::SUPER
  191. }
  192. state
  193. }
  194. }
  195. pub trait IntoKeyCode {
  196. fn into_key_code(self) -> KeyCode;
  197. }
  198. impl IntoKeyCode for KeyCode {
  199. fn into_key_code(self) -> KeyCode {
  200. self
  201. }
  202. }
  203. impl IntoKeyCode for dioxus_html::KeyCode {
  204. fn into_key_code(self) -> KeyCode {
  205. match self {
  206. dioxus_html::KeyCode::Backspace => KeyCode::Backspace,
  207. dioxus_html::KeyCode::Tab => KeyCode::Tab,
  208. dioxus_html::KeyCode::Clear => KeyCode::NumpadClear,
  209. dioxus_html::KeyCode::Enter => KeyCode::Enter,
  210. dioxus_html::KeyCode::Shift => KeyCode::ShiftLeft,
  211. dioxus_html::KeyCode::Ctrl => KeyCode::ControlLeft,
  212. dioxus_html::KeyCode::Alt => KeyCode::AltLeft,
  213. dioxus_html::KeyCode::Pause => KeyCode::Pause,
  214. dioxus_html::KeyCode::CapsLock => KeyCode::CapsLock,
  215. dioxus_html::KeyCode::Escape => KeyCode::Escape,
  216. dioxus_html::KeyCode::Space => KeyCode::Space,
  217. dioxus_html::KeyCode::PageUp => KeyCode::PageUp,
  218. dioxus_html::KeyCode::PageDown => KeyCode::PageDown,
  219. dioxus_html::KeyCode::End => KeyCode::End,
  220. dioxus_html::KeyCode::Home => KeyCode::Home,
  221. dioxus_html::KeyCode::LeftArrow => KeyCode::ArrowLeft,
  222. dioxus_html::KeyCode::UpArrow => KeyCode::ArrowUp,
  223. dioxus_html::KeyCode::RightArrow => KeyCode::ArrowRight,
  224. dioxus_html::KeyCode::DownArrow => KeyCode::ArrowDown,
  225. dioxus_html::KeyCode::Insert => KeyCode::Insert,
  226. dioxus_html::KeyCode::Delete => KeyCode::Delete,
  227. dioxus_html::KeyCode::Num0 => KeyCode::Numpad0,
  228. dioxus_html::KeyCode::Num1 => KeyCode::Numpad1,
  229. dioxus_html::KeyCode::Num2 => KeyCode::Numpad2,
  230. dioxus_html::KeyCode::Num3 => KeyCode::Numpad3,
  231. dioxus_html::KeyCode::Num4 => KeyCode::Numpad4,
  232. dioxus_html::KeyCode::Num5 => KeyCode::Numpad5,
  233. dioxus_html::KeyCode::Num6 => KeyCode::Numpad6,
  234. dioxus_html::KeyCode::Num7 => KeyCode::Numpad7,
  235. dioxus_html::KeyCode::Num8 => KeyCode::Numpad8,
  236. dioxus_html::KeyCode::Num9 => KeyCode::Numpad9,
  237. dioxus_html::KeyCode::A => KeyCode::KeyA,
  238. dioxus_html::KeyCode::B => KeyCode::KeyB,
  239. dioxus_html::KeyCode::C => KeyCode::KeyC,
  240. dioxus_html::KeyCode::D => KeyCode::KeyD,
  241. dioxus_html::KeyCode::E => KeyCode::KeyE,
  242. dioxus_html::KeyCode::F => KeyCode::KeyF,
  243. dioxus_html::KeyCode::G => KeyCode::KeyG,
  244. dioxus_html::KeyCode::H => KeyCode::KeyH,
  245. dioxus_html::KeyCode::I => KeyCode::KeyI,
  246. dioxus_html::KeyCode::J => KeyCode::KeyJ,
  247. dioxus_html::KeyCode::K => KeyCode::KeyK,
  248. dioxus_html::KeyCode::L => KeyCode::KeyL,
  249. dioxus_html::KeyCode::M => KeyCode::KeyM,
  250. dioxus_html::KeyCode::N => KeyCode::KeyN,
  251. dioxus_html::KeyCode::O => KeyCode::KeyO,
  252. dioxus_html::KeyCode::P => KeyCode::KeyP,
  253. dioxus_html::KeyCode::Q => KeyCode::KeyQ,
  254. dioxus_html::KeyCode::R => KeyCode::KeyR,
  255. dioxus_html::KeyCode::S => KeyCode::KeyS,
  256. dioxus_html::KeyCode::T => KeyCode::KeyT,
  257. dioxus_html::KeyCode::U => KeyCode::KeyU,
  258. dioxus_html::KeyCode::V => KeyCode::KeyV,
  259. dioxus_html::KeyCode::W => KeyCode::KeyW,
  260. dioxus_html::KeyCode::X => KeyCode::KeyX,
  261. dioxus_html::KeyCode::Y => KeyCode::KeyY,
  262. dioxus_html::KeyCode::Z => KeyCode::KeyZ,
  263. dioxus_html::KeyCode::Numpad0 => KeyCode::Numpad0,
  264. dioxus_html::KeyCode::Numpad1 => KeyCode::Numpad1,
  265. dioxus_html::KeyCode::Numpad2 => KeyCode::Numpad2,
  266. dioxus_html::KeyCode::Numpad3 => KeyCode::Numpad3,
  267. dioxus_html::KeyCode::Numpad4 => KeyCode::Numpad4,
  268. dioxus_html::KeyCode::Numpad5 => KeyCode::Numpad5,
  269. dioxus_html::KeyCode::Numpad6 => KeyCode::Numpad6,
  270. dioxus_html::KeyCode::Numpad7 => KeyCode::Numpad7,
  271. dioxus_html::KeyCode::Numpad8 => KeyCode::Numpad8,
  272. dioxus_html::KeyCode::Numpad9 => KeyCode::Numpad9,
  273. dioxus_html::KeyCode::Multiply => KeyCode::NumpadMultiply,
  274. dioxus_html::KeyCode::Add => KeyCode::NumpadAdd,
  275. dioxus_html::KeyCode::Subtract => KeyCode::NumpadSubtract,
  276. dioxus_html::KeyCode::DecimalPoint => KeyCode::NumpadDecimal,
  277. dioxus_html::KeyCode::Divide => KeyCode::NumpadDivide,
  278. dioxus_html::KeyCode::F1 => KeyCode::F1,
  279. dioxus_html::KeyCode::F2 => KeyCode::F2,
  280. dioxus_html::KeyCode::F3 => KeyCode::F3,
  281. dioxus_html::KeyCode::F4 => KeyCode::F4,
  282. dioxus_html::KeyCode::F5 => KeyCode::F5,
  283. dioxus_html::KeyCode::F6 => KeyCode::F6,
  284. dioxus_html::KeyCode::F7 => KeyCode::F7,
  285. dioxus_html::KeyCode::F8 => KeyCode::F8,
  286. dioxus_html::KeyCode::F9 => KeyCode::F9,
  287. dioxus_html::KeyCode::F10 => KeyCode::F10,
  288. dioxus_html::KeyCode::F11 => KeyCode::F11,
  289. dioxus_html::KeyCode::F12 => KeyCode::F12,
  290. dioxus_html::KeyCode::NumLock => KeyCode::NumLock,
  291. dioxus_html::KeyCode::ScrollLock => KeyCode::ScrollLock,
  292. dioxus_html::KeyCode::Semicolon => KeyCode::Semicolon,
  293. dioxus_html::KeyCode::EqualSign => KeyCode::Equal,
  294. dioxus_html::KeyCode::Comma => KeyCode::Comma,
  295. dioxus_html::KeyCode::Period => KeyCode::Period,
  296. dioxus_html::KeyCode::ForwardSlash => KeyCode::Slash,
  297. dioxus_html::KeyCode::GraveAccent => KeyCode::Backquote,
  298. dioxus_html::KeyCode::OpenBracket => KeyCode::BracketLeft,
  299. dioxus_html::KeyCode::BackSlash => KeyCode::Backslash,
  300. dioxus_html::KeyCode::CloseBraket => KeyCode::BracketRight,
  301. dioxus_html::KeyCode::SingleQuote => KeyCode::Quote,
  302. key => panic!("Failed to convert {:?} to tao::keyboard::KeyCode, try using tao::keyboard::KeyCode directly", key),
  303. }
  304. }
  305. }