Skip to main content

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 { key, control: false, alternate: false }
83    }
84
85    /// Denotes one key press with the control modifier held.
86    #[must_use]
87    pub const fn control(key: Key) -> Self {
88        Self { key, control: true, alternate: false }
89    }
90
91    /// Observes whether any modifier was held.
92    #[must_use]
93    pub const fn modified(self) -> bool {
94        self.control || self.alternate
95    }
96
97    /// Names the keystroke the way a menu states it.
98    #[must_use]
99    pub fn label(self) -> String {
100        // A modified character names itself in upper case, the way keyboards print it.
101        let key = if self.modified() { self.key.label().to_uppercase() } else { self.key.label() };
102        match (self.control, self.alternate) {
103            (false, false) => key,
104            (true, false) => format!("Ctrl+{key}"),
105            (false, true) => format!("Alt+{key}"),
106            (true, true) => format!("Ctrl+Alt+{key}"),
107        }
108    }
109}
110
111/// Names the keystroke that leaves the application from every page.
112pub const EXIT_KEYSTROKE: Keystroke = Keystroke::control(Key::Character('q'));