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::SUPER;
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::SUPER;
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 SUPER: ModifiersState = ModifiersState::SUPER;
294
295 let s = match self.0 {
296 state if state == ModifiersState::empty() => "none",
297 SUPER => "super",
298 ALT => "alt",
299 state if state == ALT | SUPER => "alt-super",
300 state if state == CONTROL => "ctrl",
301 state if state == CONTROL | SUPER => "ctrl-super",
302 state if state == CONTROL | ALT => "ctrl-alt",
303 state if state == CONTROL | ALT | SUPER => "ctrl-alt-super",
304 SHIFT => "shift",
305 state if state == SHIFT | SUPER => "shift-super",
306 state if state == SHIFT | ALT => "alt-shift",
307 state if state == SHIFT | ALT | SUPER => "alt-shift-super",
308 state if state == SHIFT | CONTROL => "ctrl-shift",
309 state if state == SHIFT | CONTROL | SUPER => "ctrl-shift-super",
310 state if state == SHIFT | CONTROL | ALT => "ctrl-alt-shift",
311 _ => "ctrl-alt-shift-super",
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-super")
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() {
331 if v.starts_with('-') {
332 *v = &v[1..];
333 }
334 }
335 };
336
337 if v.starts_with("ctrl") {
338 state |= ModifiersState::CONTROL;
339 v = &v[v.len().min(4)..];
340 adv_dash_if_not_empty(&mut v);
341 }
342 if v.starts_with("alt") {
343 state |= ModifiersState::ALT;
344 v = &v[v.len().min(3)..];
345 adv_dash_if_not_empty(&mut v);
346 }
347 if v.starts_with("shift") {
348 state |= ModifiersState::SHIFT;
349 v = &v[v.len().min(5)..];
350 adv_dash_if_not_empty(&mut v);
351 }
352 if v.starts_with("super") {
353 state |= ModifiersState::SUPER;
354 v = &v[v.len().min(5)..];
355 }
356
357 if v.is_empty() || u == "none" {
358 Ok(ModifiersStateDeser(state))
359 } else {
360 Err(E::invalid_value(
361 de::Unexpected::Str(u),
362 &"none or (sub-set of) ctrl-alt-shift-super",
363 ))
364 }
365 }
366 }
367
368 impl<'de> Deserialize<'de> for ModifiersStateDeser {
369 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
370 d.deserialize_str(ModifiersStateDeser(Default::default()))
371 }
372 }
373
374 #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord)]
375 pub(super) struct MiscRule<Str = SmolStr> {
376 #[serde(rename = "modifiers")]
377 pub(super) mods: ModifiersStateDeser,
378 #[serde(flatten)]
379 pub(super) key: ComplexKey<Str>,
380 #[serde(rename = "command")]
381 pub(super) cmd: Command,
382 }
383}
384
385#[cfg(feature = "serde")]
386mod ser {
387 use super::common::{ComplexKey, MiscRule, ModifiersStateDeser, SimpleKey};
388 use super::{Key, Shortcuts};
389 use serde::ser::{Serialize, SerializeMap, Serializer};
390
391 fn unpack_key<'a>(key: Key<&'a str>) -> Result<SimpleKey, ComplexKey<&'a str>> {
392 match key {
393 Key::Named(named) => Ok(SimpleKey::Named(named)),
394 Key::Character(c) => {
395 let mut iter = c.chars();
396 if let Some(c) = iter.next() {
397 if iter.next().is_none() {
398 return Ok(SimpleKey::Char(c));
399 }
400 }
401 Err(ComplexKey::Character(c))
402 }
403 Key::Unidentified(code) => Err(ComplexKey::Unidentified(code)),
404 Key::Dead(None) => panic!("invalid shortcut"),
405 Key::Dead(Some(c)) => Err(ComplexKey::Dead(c)),
406 }
407 }
408
409 impl Serialize for Shortcuts {
410 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
411 use std::collections::BTreeMap;
413
414 let mut serializer = s.serialize_map(Some(self.map.len() + 1))?;
415 let mut misc = Vec::new();
416
417 for (state, key_cmds) in self.map.iter() {
418 let mods = ModifiersStateDeser(*state);
419 let mut map = BTreeMap::new();
420
421 for (key, cmd) in key_cmds.iter() {
422 match unpack_key(key.as_ref()) {
423 Ok(sk) => {
424 map.insert(sk, *cmd);
425 }
426 Err(key) => {
427 let cmd = *cmd;
428 misc.push(MiscRule { mods, key, cmd });
429 }
430 }
431 }
432
433 if !map.is_empty() {
435 serializer.serialize_key(&mods)?;
436 serializer.serialize_value(&map)?;
437 }
438 }
439
440 if !misc.is_empty() {
441 serializer.serialize_key("other")?;
442 misc.sort();
443 serializer.serialize_value(&misc)?;
444 }
445 serializer.end()
446 }
447 }
448}
449
450#[cfg(feature = "serde")]
451mod deser {
452 use super::common::{MiscRule, ModifiersStateDeser, SimpleKey};
453 use super::{Command, Key, ModifiersState, Shortcuts};
454 use linear_map::LinearMap;
455 use serde::de::{self, Deserialize, DeserializeSeed, Deserializer, MapAccess, Visitor};
456 use std::collections::HashMap;
457 use std::fmt;
458
459 enum OptModifiersStateDeser {
460 State(ModifiersStateDeser),
461 Other,
462 }
463
464 impl<'de> Deserialize<'de> for OptModifiersStateDeser {
465 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
466 d.deserialize_str(OptModifiersStateDeser::Other)
467 }
468 }
469
470 impl<'de> Visitor<'de> for OptModifiersStateDeser {
471 type Value = OptModifiersStateDeser;
472
473 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
474 formatter.write_str("none or (sub-set of) ctrl-alt-shift-super or other")
475 }
476
477 fn visit_str<E: de::Error>(self, u: &str) -> Result<Self::Value, E> {
478 if u == "other" {
479 Ok(OptModifiersStateDeser::Other)
480 } else {
481 ModifiersStateDeser::visit_str(ModifiersStateDeser(Default::default()), u)
482 .map(OptModifiersStateDeser::State)
483 }
484 }
485 }
486
487 struct DeserSimple<'a>(&'a mut HashMap<Key, Command>);
488
489 impl<'a, 'de> DeserializeSeed<'de> for DeserSimple<'a> {
490 type Value = ();
491
492 fn deserialize<D: Deserializer<'de>>(self, d: D) -> Result<Self::Value, D::Error> {
493 d.deserialize_map(self)
494 }
495 }
496
497 impl<'a, 'de> Visitor<'de> for DeserSimple<'a> {
498 type Value = ();
499
500 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
501 formatter.write_str("a map")
502 }
503
504 fn visit_map<A>(self, mut reader: A) -> Result<Self::Value, A::Error>
505 where
506 A: de::MapAccess<'de>,
507 {
508 while let Some(sk) = reader.next_key::<SimpleKey>()? {
509 let key: Key = sk.into();
510 let cmd: Command = reader.next_value()?;
511 self.0.insert(key, cmd);
512 }
513
514 Ok(())
515 }
516 }
517
518 struct DeserComplex<'a>(&'a mut LinearMap<ModifiersState, HashMap<Key, Command>>);
519
520 impl<'a, 'de> DeserializeSeed<'de> for DeserComplex<'a> {
521 type Value = ();
522
523 fn deserialize<D: Deserializer<'de>>(self, d: D) -> Result<Self::Value, D::Error> {
524 d.deserialize_seq(self)
525 }
526 }
527
528 impl<'a, 'de> Visitor<'de> for DeserComplex<'a> {
529 type Value = ();
530
531 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
532 formatter.write_str("a sequence")
533 }
534
535 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
536 where
537 A: de::SeqAccess<'de>,
538 {
539 while let Some(rule) = seq.next_element::<MiscRule>()? {
540 let ModifiersStateDeser(state) = rule.mods;
541 let sub = self.0.entry(state).or_insert_with(Default::default);
542 sub.insert(rule.key.into(), rule.cmd);
543 }
544
545 Ok(())
546 }
547 }
548
549 struct ShortcutsVisitor;
550
551 impl<'de> Visitor<'de> for ShortcutsVisitor {
552 type Value = Shortcuts;
553
554 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
555 formatter.write_str("a map")
556 }
557
558 fn visit_map<A>(self, mut reader: A) -> Result<Self::Value, A::Error>
559 where
560 A: MapAccess<'de>,
561 {
562 let mut map = LinearMap::<ModifiersState, HashMap<Key, Command>>::new();
563 while let Some(opt_state) = reader.next_key::<OptModifiersStateDeser>()? {
564 match opt_state {
565 OptModifiersStateDeser::State(ModifiersStateDeser(state)) => {
566 let sub = map.entry(state).or_insert_with(Default::default);
567 reader.next_value_seed(DeserSimple(sub))?;
568 }
569 OptModifiersStateDeser::Other => {
570 reader.next_value_seed(DeserComplex(&mut map))?;
571 }
572 }
573 }
574
575 Ok(Shortcuts { map })
576 }
577 }
578
579 impl<'de> Deserialize<'de> for Shortcuts {
580 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
581 d.deserialize_map(ShortcutsVisitor)
582 }
583 }
584}