keyboard_types/
key.rs

1use alloc::string::{String, ToString};
2use core::fmt;
3use core::str::FromStr;
4
5use crate::{first_char, NamedKey};
6
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9
10/// The value received from the keypress.
11#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
12#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
13pub enum Key {
14    /// A key string that corresponds to the character typed by the user,
15    /// taking into account the user’s current locale setting, modifier state,
16    /// and any system-level keyboard mapping overrides that are in effect.
17    Character(String),
18    Named(NamedKey),
19}
20
21impl fmt::Display for Key {
22    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
23        match self {
24            Self::Character(s) => f.write_str(s),
25            Self::Named(k) => k.fmt(f),
26        }
27    }
28}
29
30/// Parse from string error, returned when string does not match to any [`Key`] variant.
31#[derive(Clone, Debug)]
32pub struct UnrecognizedKeyError;
33
34impl FromStr for Key {
35    type Err = UnrecognizedKeyError;
36
37    fn from_str(s: &str) -> Result<Self, Self::Err> {
38        if is_key_string(s) {
39            Ok(Self::Character(s.to_string()))
40        } else {
41            Ok(Self::Named(
42                NamedKey::from_str(s).map_err(|_| UnrecognizedKeyError)?,
43            ))
44        }
45    }
46}
47
48impl From<NamedKey> for Key {
49    fn from(value: NamedKey) -> Self {
50        Self::Named(value)
51    }
52}
53
54impl Key {
55    /// Determine a *charCode* value for a key with a character value.
56    ///
57    /// For all other keys the value is zero.
58    /// The *charCode* is an implementation specific legacy property of DOM keyboard events.
59    ///
60    /// Specification: <https://w3c.github.io/uievents/#dom-keyboardevent-charcode>
61    pub fn legacy_charcode(&self) -> u32 {
62        // Spec: event.charCode = event.key.charCodeAt(0)
63        // otherwise 0
64        match self {
65            Key::Character(ref c) => c.chars().next().unwrap_or('\0') as u32,
66            Key::Named(_) => 0,
67        }
68    }
69
70    /// Determine a *keyCode* value for a key.
71    ///
72    /// The *keyCode* is an implementation specific legacy property of DOM keyboard events.
73    ///
74    /// Specification: <https://w3c.github.io/uievents/#dom-keyboardevent-keycode>
75    pub fn legacy_keycode(&self) -> u32 {
76        match self {
77            // See: https://w3c.github.io/uievents/#fixed-virtual-key-codes
78            Key::Named(NamedKey::Backspace) => 8,
79            Key::Named(NamedKey::Tab) => 9,
80            Key::Named(NamedKey::Enter) => 13,
81            Key::Named(NamedKey::Shift) => 16,
82            Key::Named(NamedKey::Control) => 17,
83            Key::Named(NamedKey::Alt) => 18,
84            Key::Named(NamedKey::CapsLock) => 20,
85            Key::Named(NamedKey::Escape) => 27,
86            Key::Named(NamedKey::PageUp) => 33,
87            Key::Named(NamedKey::PageDown) => 34,
88            Key::Named(NamedKey::End) => 35,
89            Key::Named(NamedKey::Home) => 36,
90            Key::Named(NamedKey::ArrowLeft) => 37,
91            Key::Named(NamedKey::ArrowUp) => 38,
92            Key::Named(NamedKey::ArrowRight) => 39,
93            Key::Named(NamedKey::ArrowDown) => 40,
94            Key::Named(NamedKey::Delete) => 46,
95            Key::Character(ref c) if c.len() == 1 => match first_char(c) {
96                ' ' => 32,
97                x @ '0'..='9' => x as u32,
98                x @ 'a'..='z' => x.to_ascii_uppercase() as u32,
99                x @ 'A'..='Z' => x as u32,
100                // See: https://w3c.github.io/uievents/#optionally-fixed-virtual-key-codes
101                ';' | ':' => 186,
102                '=' | '+' => 187,
103                ',' | '<' => 188,
104                '-' | '_' => 189,
105                '.' | '>' => 190,
106                '/' | '?' => 191,
107                '`' | '~' => 192,
108                '[' | '{' => 219,
109                '\\' | '|' => 220,
110                ']' | '}' => 221,
111                '\'' | '\"' => 222,
112                _ => 0,
113            },
114            _ => 0,
115        }
116    }
117}
118
119impl Default for Key {
120    fn default() -> Self {
121        Self::Named(NamedKey::default())
122    }
123}
124
125/// Check if string can be used as a `Key::Character` _keystring_.
126///
127/// This check is simple and is meant to prevents common mistakes like mistyped keynames
128/// (e.g. `Ennter`) from being recognized as characters.
129fn is_key_string(s: &str) -> bool {
130    s.chars().all(|c| !c.is_control()) && s.chars().skip(1).all(|c| !c.is_ascii())
131}
132
133#[cfg(test)]
134mod test {
135    use super::*;
136
137    #[test]
138    fn test_is_key_string() {
139        assert!(is_key_string("A"));
140        assert!(!is_key_string("AA"));
141        assert!(!is_key_string("	"));
142    }
143
144    #[test]
145    fn into() {
146        assert_eq!(Key::Named(NamedKey::Enter), NamedKey::Enter.into());
147    }
148}