rsynko_ui/key.rs
1/// Names one key independently of the mechanism that reports it.
2#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3pub enum Key {
4 /// Names the key moving a cursor toward the preceding value.
5 Up,
6 /// Names the key moving a cursor toward the following value.
7 Down,
8 /// Names the key moving a text cursor toward the preceding scalar.
9 Left,
10 /// Names the key moving a text cursor toward the following scalar.
11 Right,
12 /// Names the key activating the selected value.
13 Enter,
14 /// Names the key leaving the current page.
15 Escape,
16 /// Names the key deleting before a text cursor.
17 Backspace,
18 /// Names the key deleting at a text cursor.
19 Delete,
20 /// Names the key moving a text cursor to its line beginning.
21 Home,
22 /// Names the key moving a text cursor to its line end.
23 End,
24 /// Names one typed Unicode scalar.
25 Character(char),
26}
27
28impl Key {
29 /// Names the key the way a menu states it.
30 #[must_use]
31 pub fn label(self) -> String {
32 match self {
33 Self::Up => "↑".to_owned(),
34 Self::Down => "↓".to_owned(),
35 Self::Left => "←".to_owned(),
36 Self::Right => "→".to_owned(),
37 Self::Enter => "Enter".to_owned(),
38 Self::Escape => "Esc".to_owned(),
39 Self::Backspace => "Backspace".to_owned(),
40 Self::Delete => "Del".to_owned(),
41 Self::Home => "Home".to_owned(),
42 Self::End => "End".to_owned(),
43 Self::Character(' ') => "Space".to_owned(),
44 Self::Character(character) => character.to_string(),
45 }
46 }
47
48 /// Observes the scalar a typed key contributes to a text draft.
49 #[must_use]
50 pub const fn typed(self) -> Option<char> {
51 match self {
52 Self::Character(character) => Some(character),
53 Self::Up
54 | Self::Down
55 | Self::Left
56 | Self::Right
57 | Self::Enter
58 | Self::Escape
59 | Self::Backspace
60 | Self::Delete
61 | Self::Home
62 | Self::End => None,
63 }
64 }
65}
66
67/// Denotes one key press together with the modifiers that change what it denotes.
68#[derive(Clone, Copy, Debug, PartialEq, Eq)]
69pub struct Keystroke {
70 /// Names the pressed key.
71 pub key: Key,
72 /// States whether the control modifier was held.
73 pub control: bool,
74 /// States whether the alternate modifier was held.
75 pub alternate: bool,
76}
77
78impl Keystroke {
79 /// Denotes one unmodified key press.
80 #[must_use]
81 pub const fn plain(key: Key) -> Self {
82 Self {
83 key,
84 control: false,
85 alternate: false,
86 }
87 }
88
89 /// Denotes one key press with the control modifier held.
90 #[must_use]
91 pub const fn control(key: Key) -> Self {
92 Self {
93 key,
94 control: true,
95 alternate: false,
96 }
97 }
98
99 /// Observes whether any modifier was held.
100 #[must_use]
101 pub const fn modified(self) -> bool {
102 self.control || self.alternate
103 }
104
105 /// Names the keystroke the way a menu states it.
106 #[must_use]
107 pub fn label(self) -> String {
108 // A modified character names itself in upper case, the way keyboards print it.
109 let key = if self.modified() {
110 self.key.label().to_uppercase()
111 } else {
112 self.key.label()
113 };
114 match (self.control, self.alternate) {
115 (false, false) => key,
116 (true, false) => format!("Ctrl+{key}"),
117 (false, true) => format!("Alt+{key}"),
118 (true, true) => format!("Ctrl+Alt+{key}"),
119 }
120 }
121}
122
123/// Names the keystroke that leaves the application from every page.
124pub const EXIT_KEYSTROKE: Keystroke = Keystroke::control(Key::Character('q'));