teksilo_widgets/
keystroke_format.rs1use teksilo_core::event::{Key, Modifiers};
24use teksilo_core::shortcut::KeyStroke;
25#[cfg(not(target_os = "macos"))]
26use teksilo_i18n::tr_widget;
27
28pub fn format_keystroke(keystroke: KeyStroke) -> String {
31 let parts = keystroke_parts(keystroke);
32 #[cfg(target_os = "macos")]
35 {
36 parts.concat()
37 }
38 #[cfg(not(target_os = "macos"))]
39 {
40 parts.join(&tr_widget!(keystroke_separator()).resolve_now())
41 }
42}
43
44pub fn keystroke_parts(keystroke: KeyStroke) -> Vec<String> {
51 let mut parts = modifier_labels(keystroke.modifiers);
52 parts.push(key_label(keystroke.key));
53 parts
54}
55
56pub(crate) fn command_modifier_labels() -> (String, String) {
61 #[cfg(target_os = "macos")]
62 {
63 ("\u{2318}".to_string(), "\u{2325}".to_string())
64 }
65 #[cfg(not(target_os = "macos"))]
66 {
67 (
68 tr_widget!(keystroke_modifier_ctrl()).resolve_now(),
69 tr_widget!(keystroke_modifier_alt()).resolve_now(),
70 )
71 }
72}
73
74#[cfg(target_os = "macos")]
75fn modifier_labels(modifiers: Modifiers) -> Vec<String> {
76 let mut out = Vec::new();
79 if modifiers.ctrl() {
80 out.push("\u{2303}".to_string());
81 }
82 if modifiers.alt() {
83 out.push("\u{2325}".to_string());
84 }
85 if modifiers.shift() {
86 out.push("\u{21E7}".to_string());
87 }
88 if modifiers.super_key() {
89 out.push("\u{2318}".to_string());
90 }
91 out
92}
93
94#[cfg(not(target_os = "macos"))]
95fn modifier_labels(modifiers: Modifiers) -> Vec<String> {
96 let mut out = Vec::new();
101 if modifiers.ctrl() {
102 out.push(tr_widget!(keystroke_modifier_ctrl()).resolve_now());
103 }
104 if modifiers.shift() {
105 out.push(tr_widget!(keystroke_modifier_shift()).resolve_now());
106 }
107 if modifiers.alt() {
108 out.push(tr_widget!(keystroke_modifier_alt()).resolve_now());
109 }
110 if modifiers.super_key() {
111 out.push(tr_widget!(keystroke_modifier_super()).resolve_now());
112 }
113 out
114}
115
116#[cfg(target_os = "macos")]
118pub(crate) fn key_label(key: Key) -> String {
119 match key {
120 Key::ArrowUp => "\u{2191}".to_string(),
121 Key::ArrowDown => "\u{2193}".to_string(),
122 Key::ArrowLeft => "\u{2190}".to_string(),
123 Key::ArrowRight => "\u{2192}".to_string(),
124 Key::Enter => "\u{21A9}".to_string(),
125 Key::Backspace => "\u{232B}".to_string(),
126 Key::Delete => "\u{2326}".to_string(),
127 Key::Escape => "\u{238B}".to_string(),
128 Key::Space => "Space".to_string(),
129 Key::Tab => "\u{21E5}".to_string(),
130 other => other.to_string(),
131 }
132}
133
134#[cfg(not(target_os = "macos"))]
136pub(crate) fn key_label(key: Key) -> String {
137 let label = match key {
143 Key::Space => Some(tr_widget!(keystroke_key_space()).resolve_now()),
144 Key::Enter => Some(tr_widget!(keystroke_key_enter()).resolve_now()),
145 Key::Escape => Some(tr_widget!(keystroke_key_escape()).resolve_now()),
146 Key::Tab => Some(tr_widget!(keystroke_key_tab()).resolve_now()),
147 Key::Backspace => Some(tr_widget!(keystroke_key_backspace()).resolve_now()),
148 Key::Delete => Some(tr_widget!(keystroke_key_delete()).resolve_now()),
149 Key::ArrowUp => Some(tr_widget!(keystroke_key_arrow_up()).resolve_now()),
150 Key::ArrowDown => Some(tr_widget!(keystroke_key_arrow_down()).resolve_now()),
151 Key::ArrowLeft => Some(tr_widget!(keystroke_key_arrow_left()).resolve_now()),
152 Key::ArrowRight => Some(tr_widget!(keystroke_key_arrow_right()).resolve_now()),
153 Key::Home => Some(tr_widget!(keystroke_key_home()).resolve_now()),
154 Key::End => Some(tr_widget!(keystroke_key_end()).resolve_now()),
155 Key::PageUp => Some(tr_widget!(keystroke_key_page_up()).resolve_now()),
156 Key::PageDown => Some(tr_widget!(keystroke_key_page_down()).resolve_now()),
157 _ => None,
158 };
159 label.unwrap_or_else(|| key.to_string())
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[test]
167 #[cfg(not(target_os = "macos"))]
168 fn non_macos_uses_translated_modifiers() {
169 assert_eq!(format_keystroke(KeyStroke::ctrl(Key::S)), "Ctrl+S");
174 assert_eq!(
175 format_keystroke(KeyStroke::ctrl_shift(Key::Z)),
176 "Ctrl+Shift+Z"
177 );
178 assert_eq!(format_keystroke(KeyStroke::alt(Key::F4)), "Alt+F4");
179 }
180
181 #[test]
182 #[cfg(not(target_os = "macos"))]
183 fn non_macos_named_keys_route_through_bundle() {
184 assert_eq!(format_keystroke(KeyStroke::ctrl(Key::Enter)), "Ctrl+Enter");
188 assert_eq!(
189 format_keystroke(KeyStroke::new(Key::Escape, Modifiers::NONE)),
190 "Esc"
191 );
192 assert_eq!(
193 format_keystroke(KeyStroke::ctrl(Key::ArrowLeft)),
194 "Ctrl+Left"
195 );
196 assert_eq!(
197 format_keystroke(KeyStroke::new(Key::Tab, Modifiers::SHIFT)),
198 "Shift+Tab"
199 );
200 }
201
202 #[test]
203 fn parts_are_the_keys_of_the_formatted_chord() {
204 let ks = KeyStroke::new(Key::Z, Modifiers::CTRL | Modifiers::SHIFT);
205 let parts = keystroke_parts(ks);
206 assert_eq!(parts.len(), 3, "two modifiers, one key: {parts:?}");
207 assert_eq!(parts.last().map(String::as_str), Some("Z"));
208 #[cfg(not(target_os = "macos"))]
209 assert_eq!(parts.join("+"), format_keystroke(ks));
210 #[cfg(target_os = "macos")]
211 assert_eq!(parts.concat(), format_keystroke(ks));
212 }
213
214 #[test]
215 #[cfg(target_os = "macos")]
216 fn macos_uses_symbols() {
217 assert_eq!(
218 format_keystroke(KeyStroke::new(Key::S, Modifiers::SUPER)),
219 "\u{2318}S"
220 );
221 assert_eq!(
222 format_keystroke(KeyStroke::new(Key::Z, Modifiers::SHIFT | Modifiers::SUPER)),
223 "\u{21E7}\u{2318}Z"
224 );
225 }
226}