1use crate::{KeyCode, KeyModifiers, ModifierKey};
2
3#[derive(Debug, Clone)]
5pub struct Binding {
6 pub key: KeyCode,
8 pub modifiers: Option<KeyModifiers>,
10 pub display: String,
12 pub description: String,
14 pub visible: bool,
16}
17
18#[derive(Debug, Clone, Default)]
31pub struct KeyMap {
32 pub bindings: Vec<Binding>,
34}
35
36impl KeyMap {
37 pub fn new() -> Self {
39 Self::default()
40 }
41
42 pub fn bind(mut self, key: char, description: &str) -> Self {
44 self.bindings.push(Binding {
45 key: KeyCode::Char(key),
46 modifiers: None,
47 display: key.to_string(),
48 description: description.to_string(),
49 visible: true,
50 });
51 self
52 }
53
54 pub fn bind_code(mut self, key: KeyCode, description: &str) -> Self {
56 self.bindings.push(Binding {
57 display: display_for_key_code(&key),
58 key,
59 modifiers: None,
60 description: description.to_string(),
61 visible: true,
62 });
63 self
64 }
65
66 pub fn bind_mod(mut self, key: char, mods: KeyModifiers, description: &str) -> Self {
68 self.bindings.push(Binding {
69 key: KeyCode::Char(key),
70 modifiers: Some(mods),
71 display: display_for_mod_char(mods, key),
72 description: description.to_string(),
73 visible: true,
74 });
75 self
76 }
77
78 pub fn bind_code_mod(mut self, key: KeyCode, mods: KeyModifiers, description: &str) -> Self {
92 let display = display_for_code_mod(&key, mods);
93 self.bindings.push(Binding {
94 key,
95 modifiers: Some(mods),
96 display,
97 description: description.to_string(),
98 visible: true,
99 });
100 self
101 }
102
103 pub fn bind_hidden(mut self, key: char, description: &str) -> Self {
105 self.bindings.push(Binding {
106 key: KeyCode::Char(key),
107 modifiers: None,
108 display: key.to_string(),
109 description: description.to_string(),
110 visible: false,
111 });
112 self
113 }
114
115 pub fn visible_bindings(&self) -> impl Iterator<Item = &Binding> {
117 self.bindings.iter().filter(|binding| binding.visible)
118 }
119}
120
121fn display_for_key_code(key: &KeyCode) -> String {
122 match key {
123 KeyCode::Char(c) => c.to_string(),
124 KeyCode::Enter => "Enter".to_string(),
125 KeyCode::Backspace => "Backspace".to_string(),
126 KeyCode::Tab => "Tab".to_string(),
127 KeyCode::BackTab => "Shift+Tab".to_string(),
128 KeyCode::Esc => "Esc".to_string(),
129 KeyCode::Up => "↑".to_string(),
130 KeyCode::Down => "↓".to_string(),
131 KeyCode::Left => "←".to_string(),
132 KeyCode::Right => "→".to_string(),
133 KeyCode::Home => "Home".to_string(),
134 KeyCode::End => "End".to_string(),
135 KeyCode::PageUp => "PgUp".to_string(),
136 KeyCode::PageDown => "PgDn".to_string(),
137 KeyCode::Delete => "Del".to_string(),
138 KeyCode::Insert => "Ins".to_string(),
139 KeyCode::Null => "Null".to_string(),
140 KeyCode::CapsLock => "CapsLock".to_string(),
141 KeyCode::ScrollLock => "ScrollLock".to_string(),
142 KeyCode::NumLock => "NumLock".to_string(),
143 KeyCode::PrintScreen => "PrtSc".to_string(),
144 KeyCode::Pause => "Pause".to_string(),
145 KeyCode::Menu => "Menu".to_string(),
146 KeyCode::KeypadBegin => "KP5".to_string(),
147 KeyCode::F(n) => format!("F{n}"),
148 KeyCode::Modifier(m) => display_for_modifier_key(*m).to_string(),
149 }
150}
151
152fn display_for_modifier_key(m: ModifierKey) -> &'static str {
153 match m {
154 ModifierKey::LeftShift => "LShift",
155 ModifierKey::LeftCtrl => "LCtrl",
156 ModifierKey::LeftAlt => "LAlt",
157 ModifierKey::LeftSuper => "LSuper",
158 ModifierKey::RightShift => "RShift",
159 ModifierKey::RightCtrl => "RCtrl",
160 ModifierKey::RightAlt => "RAlt",
161 ModifierKey::RightSuper => "RSuper",
162 ModifierKey::LeftHyper => "LHyper",
163 ModifierKey::LeftMeta => "LMeta",
164 ModifierKey::RightHyper => "RHyper",
165 ModifierKey::RightMeta => "RMeta",
166 ModifierKey::IsoLevel3Shift => "ISO3",
167 ModifierKey::IsoLevel5Shift => "ISO5",
168 }
169}
170
171fn display_for_code_mod(key: &KeyCode, mods: KeyModifiers) -> String {
172 let mut parts: Vec<&str> = Vec::new();
173 if mods.contains(KeyModifiers::CONTROL) {
174 parts.push("Ctrl");
175 }
176 if mods.contains(KeyModifiers::ALT) {
177 parts.push("Alt");
178 }
179 if mods.contains(KeyModifiers::SHIFT) {
180 parts.push("Shift");
181 }
182 if mods.contains(KeyModifiers::SUPER) {
183 parts.push("Super");
184 }
185 if mods.contains(KeyModifiers::HYPER) {
186 parts.push("Hyper");
187 }
188 if mods.contains(KeyModifiers::META) {
189 parts.push("Meta");
190 }
191
192 let key_label = display_for_key_code(key);
193 if parts.is_empty() {
194 key_label
195 } else {
196 format!("{}+{}", parts.join("+"), key_label)
197 }
198}
199
200fn display_for_mod_char(mods: KeyModifiers, key: char) -> String {
201 let mut parts: Vec<&str> = Vec::new();
202 if mods.contains(KeyModifiers::CONTROL) {
203 parts.push("Ctrl");
204 }
205 if mods.contains(KeyModifiers::ALT) {
206 parts.push("Alt");
207 }
208 if mods.contains(KeyModifiers::SHIFT) {
209 parts.push("Shift");
210 }
211 if mods.contains(KeyModifiers::SUPER) {
212 parts.push("Super");
213 }
214 if mods.contains(KeyModifiers::HYPER) {
215 parts.push("Hyper");
216 }
217 if mods.contains(KeyModifiers::META) {
218 parts.push("Meta");
219 }
220
221 if parts.is_empty() {
222 key.to_string()
223 } else {
224 format!("{}+{}", parts.join("+"), key.to_ascii_uppercase())
225 }
226}
227
228pub trait WidgetKeyHelp {
278 fn key_help(&self) -> &'static [(&'static str, &'static str)];
282}
283
284#[derive(Debug, Clone)]
291pub struct PublishedKeymap {
292 pub name: &'static str,
294 pub bindings: &'static [(&'static str, &'static str)],
297}
298
299impl PublishedKeymap {
300 pub const fn new(
302 name: &'static str,
303 bindings: &'static [(&'static str, &'static str)],
304 ) -> Self {
305 Self { name, bindings }
306 }
307}