ashlar_native/menu/
accelerator.rs1use ashlar_command::shortcut::{Chord, Mods};
6
7#[deprecated(note = "use ashlar_command::shortcut::Chord")]
10pub type Accelerator = Chord;
11
12#[deprecated(note = "use ashlar_command::shortcut::Mods")]
14pub type Modifiers = Mods;
15
16#[deprecated(note = "use ashlar_command::menu::menu_chord")]
18pub fn menu_accelerator(text: &str) -> Option<Chord> {
19 ashlar_command::menu::menu_chord(text)
20}
21
22#[cfg_attr(not(any(target_os = "macos", target_os = "windows")), allow(dead_code))]
25fn code_name(chord: &Chord) -> Option<String> {
26 use ashlar_command::shortcut::{Key, Named};
27 Some(match chord.key {
28 Key::Char(c) if c.is_ascii_uppercase() => format!("Key{c}"),
29 Key::Char(c) if c.is_ascii_digit() => format!("Digit{c}"),
30 Key::Char(c) => {
31 let name = match c {
32 '`' => "Backquote",
33 '\\' => "Backslash",
34 '[' => "BracketLeft",
35 ']' => "BracketRight",
36 ',' => "Comma",
37 '-' => "Minus",
38 '=' | '+' => "Equal",
40 '.' => "Period",
41 '/' => "Slash",
42 ';' => "Semicolon",
43 '\'' => "Quote",
44 _ => return None,
45 };
46 name.to_string()
47 }
48 Key::Named(n) => match n {
49 Named::Left => "ArrowLeft",
50 Named::Right => "ArrowRight",
51 Named::Up => "ArrowUp",
52 Named::Down => "ArrowDown",
53 n => n.name(),
54 }
55 .to_string(),
56 Key::F(n) => format!("F{n}"),
57 })
58}
59
60#[cfg(any(target_os = "macos", target_os = "windows"))]
62pub fn to_muda(chord: &Chord) -> Option<muda::accelerator::Accelerator> {
63 use muda::accelerator::{CMD_OR_CTRL, Code, Modifiers as M};
64 let code: Code = code_name(chord)?.parse().ok()?;
65 let mut mods = M::empty();
66 for (on, m) in [(chord.mods.command, CMD_OR_CTRL), (chord.mods.ctrl, M::CONTROL), (chord.mods.alt, M::ALT), (chord.mods.shift, M::SHIFT)] {
67 if on {
68 mods |= m;
69 }
70 }
71 Some(muda::accelerator::Accelerator::new(mods, code))
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 fn chord(text: &str) -> Chord {
79 Chord::parse(text).unwrap_or_else(|e| panic!("{e}"))
80 }
81
82 #[test]
83 fn chords_map_to_physical_key_codes() {
84 let cases = [
85 ("Mod+Shift+S", "KeyS"),
86 ("Mod+0", "Digit0"),
87 ("Mod++", "Equal"),
88 ("Mod+=", "Equal"),
89 ("Mod+Alt+;", "Semicolon"),
90 ("F5", "F5"),
91 ("Mod+Left", "ArrowLeft"),
92 ("Option+Return", "Enter"),
93 ("Mod+`", "Backquote"),
94 ("Mod+PageDown", "PageDown"),
95 ("⌘⇧[", "BracketLeft"),
96 ];
97 for (text, code) in cases {
98 assert_eq!(code_name(&chord(text)).as_deref(), Some(code), "{text}");
99 }
100 assert_eq!(code_name(&chord("Mod+é")), None);
101 }
102
103 #[cfg(any(target_os = "macos", target_os = "windows"))]
104 #[test]
105 fn chords_become_muda_accelerators() {
106 use muda::accelerator::{Accelerator as A, CMD_OR_CTRL, Code, Modifiers as M};
107 assert_eq!(to_muda(&chord("Mod+Shift+S")), Some(A::new(CMD_OR_CTRL | M::SHIFT, Code::KeyS)));
108 assert_eq!(to_muda(&chord("Ctrl+Alt+Delete")), Some(A::new(M::CONTROL | M::ALT, Code::Delete)));
109 assert_eq!(to_muda(&chord("F12")), Some(A::new(M::empty(), Code::F12)));
110 for text in ["Mod+F24", "Mod+Insert", "Mod+Space", "Mod+Tab", "Mod+Escape", "Mod+/", "Mod+'", "Mod+\\", "Mod+,", "Mod+."] {
112 assert!(to_muda(&chord(text)).is_some(), "{text}");
113 }
114 assert!(to_muda(&chord("Mod+é")).is_none());
115 }
116}