1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// src/components/input_key.rs
// Declare a key enum from one table, so the variant list, the serde spelling,
// the short label, and the exhaustive `ALL` array cannot drift apart. Each
// entry is `Variant` (its label is the variant name) or `Variant => "label"`
// when the settings menu shows something shorter.
// The settings-menu label for one table entry: the override when given, the
// variant name otherwise.
macro_rules! key_label {
($variant:ident) => {
stringify!($variant)
};
($variant:ident => $label:literal) => {
$label
};
}
macro_rules! define_keys {
($($variant:ident $(=> $label:literal)?),* $(,)?) => {
/// A canonical, backend-agnostic keyboard key.
///
/// Each rendering backend maps its native key codes (macOS NSEvent key
/// codes, Windows virtual keys, GLFW keys) to and from this enum, so a
/// key binding can be stored and shown the same way everywhere. Unit
/// variants serialize to their name, so a persisted binding survives a
/// build.
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize,
)]
// Each variant is one key name; the vocabulary is described above
// rather than restated per variant.
#[expect(missing_docs, reason = "each variant is one key name; the vocabulary is documented on the enum")]
pub enum InputKey {
$($variant),*
}
impl InputKey {
/// Every declared key, in declaration order.
pub const ALL: &'static [InputKey] = &[$(InputKey::$variant),*];
/// The canonical variant name, matching the serialized form and how
/// a [KeyBinding](#keybinding) stores its `key` (e.g. `"W"`,
/// `"Space"`, `"Enter"`, `"Control"`). Unlike
/// [display_name](#method.display_name) this is the exact
/// enum-variant spelling, so it round-trips with serde.
pub fn name(self) -> &'static str {
match self {
$(InputKey::$variant => stringify!($variant)),*
}
}
/// A short label for the settings menu (e.g. `"W"`, `"Space"`,
/// `"Ctrl"`). Defaults to [name](#method.name) unless the key
/// declared a shorter one.
pub fn display_name(self) -> &'static str {
match self {
$(InputKey::$variant => key_label!($variant $(=> $label)?)),*
}
}
}
};
}
define_keys! {
A, B, C, D, E, F, G, H, I, J, K, L, M,
N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
Num0 => "0",
Num1 => "1",
Num2 => "2",
Num3 => "3",
Num4 => "4",
Num5 => "5",
Num6 => "6",
Num7 => "7",
Num8 => "8",
Num9 => "9",
Space,
Tab,
Enter,
Backspace => "Bksp",
Delete => "Del",
Shift,
Control => "Ctrl",
Alt,
Up,
Down,
Left,
Right,
Minus => "-",
Equals => "=",
LeftBracket => "[",
RightBracket => "]",
Backslash => "\\",
Semicolon => ";",
Quote => "'",
Comma => ",",
Period => ".",
Slash => "/",
Backtick => "`",
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::format;
#[test]
fn serializes_to_variant_name() {
// A unit variant serializes to its name, so a persisted binding is
// readable and stable across builds.
let json = serde_json::to_string(&InputKey::W).unwrap();
assert_eq!(json, "\"W\"");
let back: InputKey = serde_json::from_str(&json).unwrap();
assert_eq!(back, InputKey::W);
}
#[test]
fn display_names_are_short() {
assert_eq!(InputKey::W.display_name(), "W");
assert_eq!(InputKey::Space.display_name(), "Space");
assert_eq!(InputKey::Shift.display_name(), "Shift");
assert_eq!(InputKey::Num1.display_name(), "1");
assert_eq!(InputKey::Backspace.display_name(), "Bksp");
assert_eq!(InputKey::Control.display_name(), "Ctrl");
assert_eq!(InputKey::Minus.display_name(), "-");
}
#[test]
fn all_variants_cover_name_and_display() {
// For every variant: name() and display_name() are non-empty, name()
// equals the serde spelling, and the binding round-trips. This walks
// both full match statements, not just a hand-picked sample.
for &key in InputKey::ALL {
assert!(!key.name().is_empty(), "name empty for {key:?}");
assert!(!key.display_name().is_empty(), "display empty for {key:?}");
let json = serde_json::to_string(&key).unwrap();
assert_eq!(
json,
format!("\"{}\"", key.name()),
"serde vs name for {key:?}"
);
let back: InputKey = serde_json::from_str(&json).unwrap();
assert_eq!(back, key, "round trip for {key:?}");
}
}
#[test]
fn variant_names_are_unique() {
// Names double as persisted identifiers, so no two variants may share
// one.
let mut seen = alloc::collections::BTreeSet::new();
for &key in InputKey::ALL {
assert!(seen.insert(key.name()), "duplicate name {}", key.name());
}
}
}