1use crate::event::{Command, Key, ModifiersState};
9use linear_map::LinearMap;
10use std::collections::HashMap;
11use winit::keyboard::NamedKey;
12
13#[allow(clippy::derive_partial_eq_without_eq)]
15#[derive(Clone, Debug, PartialEq)]
16pub struct Shortcuts {
17 map: LinearMap<ModifiersState, HashMap<Key, Command>>,
19}
20
21impl Shortcuts {
22 #[inline]
24 pub fn empty() -> Self {
25 Shortcuts {
26 map: Default::default(),
27 }
28 }
29
30 #[inline]
32 pub fn platform_defaults() -> Self {
33 let mut s = Self::empty();
34 s.load_platform_defaults();
35 s
36 }
37
38 pub fn load_platform_defaults(&mut self) {
40 #[cfg(target_os = "macos")]
41 const CMD: ModifiersState = ModifiersState::META;
42 #[cfg(not(target_os = "macos"))]
43 const CMD: ModifiersState = ModifiersState::CONTROL;
44
45 #[cfg(not(target_os = "macos"))]
47 {
48 let modifiers = ModifiersState::empty();
49 let map = self.map.entry(modifiers).or_insert_with(Default::default);
50 let shortcuts = [
51 (NamedKey::F1.into(), Command::Help),
52 (NamedKey::F2.into(), Command::Rename),
53 (NamedKey::F3.into(), Command::FindNext),
54 (NamedKey::F5.into(), Command::Refresh),
55 (NamedKey::F7.into(), Command::SpellCheck),
56 (NamedKey::F8.into(), Command::Debug),
57 (NamedKey::F10.into(), Command::Menu),
58 (NamedKey::F11.into(), Command::Fullscreen),
59 ];
60 map.extend(shortcuts.iter().cloned());
61 }
62
63 #[cfg(not(target_os = "macos"))]
65 {
66 let modifiers = ModifiersState::SHIFT;
67 let map = self.map.entry(modifiers).or_insert_with(Default::default);
68 map.insert(NamedKey::F3.into(), Command::FindPrevious);
69 }
70
71 let modifiers = ModifiersState::ALT;
73 let map = self.map.entry(modifiers).or_insert_with(Default::default);
74 #[cfg(not(target_os = "macos"))]
75 {
76 let shortcuts = [
77 (NamedKey::F4.into(), Command::Close),
78 (NamedKey::ArrowLeft.into(), Command::NavPrevious),
79 (NamedKey::ArrowRight.into(), Command::NavNext),
80 (NamedKey::ArrowUp.into(), Command::NavParent),
81 (NamedKey::ArrowDown.into(), Command::NavDown),
82 ];
83 map.extend(shortcuts.iter().cloned());
84 }
85 #[cfg(target_os = "macos")]
86 {
87 let shortcuts = [
89 (NamedKey::ArrowLeft.into(), Command::WordLeft),
90 (NamedKey::ArrowRight.into(), Command::WordRight),
91 ];
92
93 map.insert(NamedKey::Delete.into(), Command::DelWordBack);
94 map.extend(shortcuts.iter().cloned());
95
96 let modifiers = ModifiersState::SHIFT | ModifiersState::ALT;
98 let map = self.map.entry(modifiers).or_insert_with(Default::default);
99 map.extend(shortcuts.iter().cloned());
100 }
101
102 let map = self.map.entry(CMD).or_insert_with(Default::default);
104 let shortcuts = [
105 (Key::Character("a".into()), Command::SelectAll),
106 (Key::Character("b".into()), Command::Bold),
107 (Key::Character("c".into()), Command::Copy),
108 (Key::Character("f".into()), Command::Find),
109 (Key::Character("i".into()), Command::Italic),
110 (Key::Character("k".into()), Command::Link),
111 (Key::Character("n".into()), Command::New),
112 (Key::Character("o".into()), Command::Open),
113 (Key::Character("p".into()), Command::Print),
114 (Key::Character("s".into()), Command::Save),
115 (Key::Character("t".into()), Command::TabNew),
116 (Key::Character("u".into()), Command::Underline),
117 (Key::Character("v".into()), Command::Paste),
118 (Key::Character("w".into()), Command::Close),
119 (Key::Character("x".into()), Command::Cut),
120 (Key::Character("z".into()), Command::Undo),
121 (NamedKey::Tab.into(), Command::TabNext),
122 ];
123 map.extend(shortcuts.iter().cloned());
124 #[cfg(target_os = "macos")]
125 {
126 let shortcuts = [
127 (Key::Character("g".into()), Command::FindNext),
128 (NamedKey::ArrowUp.into(), Command::DocHome),
129 (NamedKey::ArrowDown.into(), Command::DocEnd),
130 (NamedKey::ArrowLeft.into(), Command::Home),
131 (NamedKey::ArrowRight.into(), Command::End),
132 ];
133 map.extend(shortcuts.iter().cloned());
134 }
135 #[cfg(not(target_os = "macos"))]
136 {
137 let shortcuts = [
138 (Key::Character("q".into()), Command::Exit),
139 (Key::Character("r".into()), Command::FindReplace),
140 ];
141 map.extend(shortcuts.iter().cloned());
142
143 let shortcuts = [
144 (NamedKey::ArrowUp.into(), Command::ViewUp),
145 (NamedKey::ArrowDown.into(), Command::ViewDown),
146 (NamedKey::ArrowLeft.into(), Command::WordLeft),
147 (NamedKey::ArrowRight.into(), Command::WordRight),
148 (NamedKey::Backspace.into(), Command::DelWordBack),
149 (NamedKey::Delete.into(), Command::DelWord),
150 (NamedKey::Home.into(), Command::DocHome),
151 (NamedKey::End.into(), Command::DocEnd),
152 (NamedKey::PageUp.into(), Command::TabPrevious),
153 (NamedKey::PageDown.into(), Command::TabNext),
154 ];
155 map.extend(shortcuts.iter().cloned());
156
157 let modifiers = ModifiersState::SHIFT | CMD;
159 let map = self.map.entry(modifiers).or_insert_with(Default::default);
160 map.extend(shortcuts.iter().cloned());
161 }
162
163 #[cfg(target_os = "macos")]
165 {
166 let modifiers = ModifiersState::CONTROL | ModifiersState::META;
167 let map = self.map.entry(modifiers).or_insert_with(Default::default);
168 map.insert(Key::Character("f".into()), Command::Fullscreen);
169 }
170
171 let modifiers = ModifiersState::SHIFT | CMD;
173 let map = self.map.entry(modifiers).or_insert_with(Default::default);
174 let shortcuts = [
175 (Key::Character("a".into()), Command::Deselect),
176 (Key::Character("z".into()), Command::Redo),
177 (NamedKey::Tab.into(), Command::TabPrevious),
178 ];
179 map.extend(shortcuts.iter().cloned());
180 #[cfg(target_os = "macos")]
181 {
182 let shortcuts = [
183 (Key::Character("g".into()), Command::FindPrevious),
184 (Key::Character(":".into()), Command::SpellCheck),
185 (NamedKey::ArrowUp.into(), Command::DocHome),
186 (NamedKey::ArrowDown.into(), Command::DocEnd),
187 (NamedKey::ArrowLeft.into(), Command::Home),
188 (NamedKey::ArrowRight.into(), Command::End),
189 ];
190 map.extend(shortcuts.iter().cloned());
191 }
192
193 #[cfg(target_os = "macos")]
195 {
196 let modifiers = ModifiersState::ALT | CMD;
197 let map = self.map.entry(modifiers).or_insert_with(Default::default);
198 map.insert(Key::Character("w".into()), Command::Exit);
199 }
200 }
201
202 pub fn try_match(&self, mut modifiers: ModifiersState, key: &Key) -> Option<Command> {
208 if let Some(result) = self.map.get(&modifiers).and_then(|m| m.get(key)) {
209 return Some(*result);
210 }
211 modifiers.remove(ModifiersState::SHIFT);
212 if modifiers.is_empty() {
213 return Command::new(key);
215 }
216 None
217 }
218}
219
220#[cfg(feature = "serde")]
221mod common {
222 use super::{Command, Key, ModifiersState, NamedKey};
223 use serde::de::{self, Deserializer, Visitor};
224 use serde::ser::Serializer;
225 use serde::{Deserialize, Serialize};
226 use std::fmt;
227 use winit::keyboard::{NativeKey, SmolStr};
228
229 #[derive(Deserialize)]
231 #[serde(untagged)]
232 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
233 pub(super) enum SimpleKey {
234 Named(NamedKey),
235 Char(char),
236 }
237
238 impl From<SimpleKey> for Key<SmolStr> {
239 fn from(sk: SimpleKey) -> Self {
240 match sk {
241 SimpleKey::Named(key) => Key::Named(key),
242 SimpleKey::Char(c) => {
243 let mut buf = [0; 4];
244 let s = c.encode_utf8(&mut buf);
245 Key::Character(SmolStr::new(s))
246 }
247 }
248 }
249 }
250
251 impl Serialize for SimpleKey {
254 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
255 match self {
256 SimpleKey::Named(key) => key.serialize(s),
257 SimpleKey::Char(c) => {
258 let mut buf = [0; 4];
259 let cs = c.encode_utf8(&mut buf);
260 s.serialize_str(cs)
261 }
262 }
263 }
264 }
265
266 #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord)]
268 pub(super) enum ComplexKey<Str> {
269 Character(Str),
270 Dead(char),
271 #[serde(untagged)]
272 Unidentified(NativeKey),
273 }
274
275 impl From<ComplexKey<SmolStr>> for Key<SmolStr> {
276 fn from(ck: ComplexKey<SmolStr>) -> Self {
277 match ck {
278 ComplexKey::Character(c) => Key::Character(c),
279 ComplexKey::Dead(c) => Key::Dead(Some(c)),
280 ComplexKey::Unidentified(code) => Key::Unidentified(code),
281 }
282 }
283 }
284
285 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
286 pub(super) struct ModifiersStateDeser(pub ModifiersState);
287
288 impl Serialize for ModifiersStateDeser {
289 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
290 const SHIFT: ModifiersState = ModifiersState::SHIFT;
291 const CONTROL: ModifiersState = ModifiersState::CONTROL;
292 const ALT: ModifiersState = ModifiersState::ALT;
293 const META: ModifiersState = ModifiersState::META;
294
295 let s = match self.0 {
296 state if state == ModifiersState::empty() => "none",
297 META => "meta",
298 ALT => "alt",
299 state if state == ALT | META => "alt-meta",
300 state if state == CONTROL => "ctrl",
301 state if state == CONTROL | META => "ctrl-meta",
302 state if state == CONTROL | ALT => "ctrl-alt",
303 state if state == CONTROL | ALT | META => "ctrl-alt-meta",
304 SHIFT => "shift",
305 state if state == SHIFT | META => "shift-meta",
306 state if state == SHIFT | ALT => "alt-shift",
307 state if state == SHIFT | ALT | META => "alt-shift-meta",
308 state if state == SHIFT | CONTROL => "ctrl-shift",
309 state if state == SHIFT | CONTROL | META => "ctrl-shift-meta",
310 state if state == SHIFT | CONTROL | ALT => "ctrl-alt-shift",
311 _ => "ctrl-alt-shift-meta",
312 };
313
314 serializer.serialize_str(s)
315 }
316 }
317
318 impl<'de> Visitor<'de> for ModifiersStateDeser {
319 type Value = ModifiersStateDeser;
320
321 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
322 formatter.write_str("none or (sub-set of) ctrl-alt-shift-meta")
323 }
324
325 fn visit_str<E: de::Error>(self, u: &str) -> Result<Self::Value, E> {
326 let mut v = u;
327 let mut state = ModifiersState::empty();
328
329 let adv_dash_if_not_empty = |v: &mut &str| {
330 if !v.is_empty() && v.starts_with('-') {
331 *v = &v[1..];
332 }
333 };
334
335 if v.starts_with("ctrl") {
336 state |= ModifiersState::CONTROL;
337 v = &v[v.len().min(4)..];
338 adv_dash_if_not_empty(&mut v);
339 }
340 if v.starts_with("alt") {
341 state |= ModifiersState::ALT;
342 v = &v[v.len().min(3)..];
343 adv_dash_if_not_empty(&mut v);
344 }
345 if v.starts_with("shift") {
346 state |= ModifiersState::SHIFT;
347 v = &v[v.len().min(5)..];
348 adv_dash_if_not_empty(&mut v);
349 }
350 if v.starts_with("meta") {
351 state |= ModifiersState::META;
352 v = &v[v.len().min(5)..];
353 }
354
355 if v.is_empty() || u == "none" {
356 Ok(ModifiersStateDeser(state))
357 } else {
358 Err(E::invalid_value(
359 de::Unexpected::Str(u),
360 &"none or (sub-set of) ctrl-alt-shift-meta",
361 ))
362 }
363 }
364 }
365
366 impl<'de> Deserialize<'de> for ModifiersStateDeser {
367 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
368 d.deserialize_str(ModifiersStateDeser(Default::default()))
369 }
370 }
371
372 #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord)]
373 pub(super) struct MiscRule<Str = SmolStr> {
374 #[serde(rename = "modifiers")]
375 pub(super) mods: ModifiersStateDeser,
376 #[serde(flatten)]
377 pub(super) key: ComplexKey<Str>,
378 #[serde(rename = "command")]
379 pub(super) cmd: Command,
380 }
381}
382
383#[cfg(feature = "serde")]
384mod ser {
385 use super::common::{ComplexKey, MiscRule, ModifiersStateDeser, SimpleKey};
386 use super::{Key, Shortcuts};
387 use serde::ser::{Serialize, SerializeMap, Serializer};
388
389 fn unpack_key<'a>(key: Key<&'a str>) -> Result<SimpleKey, ComplexKey<&'a str>> {
390 match key {
391 Key::Named(named) => Ok(SimpleKey::Named(named)),
392 Key::Character(c) => {
393 let mut iter = c.chars();
394 if let Some(c) = iter.next()
395 && iter.next().is_none()
396 {
397 return Ok(SimpleKey::Char(c));
398 }
399 Err(ComplexKey::Character(c))
400 }
401 Key::Unidentified(code) => Err(ComplexKey::Unidentified(code)),
402 Key::Dead(None) => panic!("invalid shortcut"),
403 Key::Dead(Some(c)) => Err(ComplexKey::Dead(c)),
404 }
405 }
406
407 impl Serialize for Shortcuts {
408 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
409 use std::collections::BTreeMap;
411
412 let mut serializer = s.serialize_map(Some(self.map.len() + 1))?;
413 let mut misc = Vec::new();
414
415 for (state, key_cmds) in self.map.iter() {
416 let mods = ModifiersStateDeser(*state);
417 let mut map = BTreeMap::new();
418
419 for (key, cmd) in key_cmds.iter() {
420 match unpack_key(key.as_ref()) {
421 Ok(sk) => {
422 map.insert(sk, *cmd);
423 }
424 Err(key) => {
425 let cmd = *cmd;
426 misc.push(MiscRule { mods, key, cmd });
427 }
428 }
429 }
430
431 if !map.is_empty() {
433 serializer.serialize_key(&mods)?;
434 serializer.serialize_value(&map)?;
435 }
436 }
437
438 if !misc.is_empty() {
439 serializer.serialize_key("other")?;
440 misc.sort();
441 serializer.serialize_value(&misc)?;
442 }
443 serializer.end()
444 }
445 }
446}
447
448#[cfg(feature = "serde")]
449mod deser {
450 use super::common::{MiscRule, ModifiersStateDeser, SimpleKey};
451 use super::{Command, Key, ModifiersState, Shortcuts};
452 use linear_map::LinearMap;
453 use serde::de::{self, Deserialize, DeserializeSeed, Deserializer, MapAccess, Visitor};
454 use std::collections::HashMap;
455 use std::fmt;
456
457 enum OptModifiersStateDeser {
458 State(ModifiersStateDeser),
459 Other,
460 }
461
462 impl<'de> Deserialize<'de> for OptModifiersStateDeser {
463 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
464 d.deserialize_str(OptModifiersStateDeser::Other)
465 }
466 }
467
468 impl<'de> Visitor<'de> for OptModifiersStateDeser {
469 type Value = OptModifiersStateDeser;
470
471 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
472 formatter.write_str("none or (sub-set of) ctrl-alt-shift-meta or other")
473 }
474
475 fn visit_str<E: de::Error>(self, u: &str) -> Result<Self::Value, E> {
476 if u == "other" {
477 Ok(OptModifiersStateDeser::Other)
478 } else {
479 ModifiersStateDeser::visit_str(ModifiersStateDeser(Default::default()), u)
480 .map(OptModifiersStateDeser::State)
481 }
482 }
483 }
484
485 struct DeserSimple<'a>(&'a mut HashMap<Key, Command>);
486
487 impl<'a, 'de> DeserializeSeed<'de> for DeserSimple<'a> {
488 type Value = ();
489
490 fn deserialize<D: Deserializer<'de>>(self, d: D) -> Result<Self::Value, D::Error> {
491 d.deserialize_map(self)
492 }
493 }
494
495 impl<'a, 'de> Visitor<'de> for DeserSimple<'a> {
496 type Value = ();
497
498 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
499 formatter.write_str("a map")
500 }
501
502 fn visit_map<A>(self, mut reader: A) -> Result<Self::Value, A::Error>
503 where
504 A: de::MapAccess<'de>,
505 {
506 while let Some(sk) = reader.next_key::<SimpleKey>()? {
507 let key: Key = sk.into();
508 let cmd: Command = reader.next_value()?;
509 self.0.insert(key, cmd);
510 }
511
512 Ok(())
513 }
514 }
515
516 struct DeserComplex<'a>(&'a mut LinearMap<ModifiersState, HashMap<Key, Command>>);
517
518 impl<'a, 'de> DeserializeSeed<'de> for DeserComplex<'a> {
519 type Value = ();
520
521 fn deserialize<D: Deserializer<'de>>(self, d: D) -> Result<Self::Value, D::Error> {
522 d.deserialize_seq(self)
523 }
524 }
525
526 impl<'a, 'de> Visitor<'de> for DeserComplex<'a> {
527 type Value = ();
528
529 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
530 formatter.write_str("a sequence")
531 }
532
533 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
534 where
535 A: de::SeqAccess<'de>,
536 {
537 while let Some(rule) = seq.next_element::<MiscRule>()? {
538 let ModifiersStateDeser(state) = rule.mods;
539 let sub = self.0.entry(state).or_insert_with(Default::default);
540 sub.insert(rule.key.into(), rule.cmd);
541 }
542
543 Ok(())
544 }
545 }
546
547 struct ShortcutsVisitor;
548
549 impl<'de> Visitor<'de> for ShortcutsVisitor {
550 type Value = Shortcuts;
551
552 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
553 formatter.write_str("a map")
554 }
555
556 fn visit_map<A>(self, mut reader: A) -> Result<Self::Value, A::Error>
557 where
558 A: MapAccess<'de>,
559 {
560 let mut map = LinearMap::<ModifiersState, HashMap<Key, Command>>::new();
561 while let Some(opt_state) = reader.next_key::<OptModifiersStateDeser>()? {
562 match opt_state {
563 OptModifiersStateDeser::State(ModifiersStateDeser(state)) => {
564 let sub = map.entry(state).or_insert_with(Default::default);
565 reader.next_value_seed(DeserSimple(sub))?;
566 }
567 OptModifiersStateDeser::Other => {
568 reader.next_value_seed(DeserComplex(&mut map))?;
569 }
570 }
571 }
572
573 Ok(Shortcuts { map })
574 }
575 }
576
577 impl<'de> Deserialize<'de> for Shortcuts {
578 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
579 d.deserialize_map(ShortcutsVisitor)
580 }
581 }
582}