1use crate::{KeyCode, KeyModifiers};
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>,
33}
34
35impl KeyMap {
36 pub fn new() -> Self {
37 Self::default()
38 }
39
40 pub fn bind(mut self, key: char, description: &str) -> Self {
42 self.bindings.push(Binding {
43 key: KeyCode::Char(key),
44 modifiers: None,
45 display: key.to_string(),
46 description: description.to_string(),
47 visible: true,
48 });
49 self
50 }
51
52 pub fn bind_code(mut self, key: KeyCode, description: &str) -> Self {
54 self.bindings.push(Binding {
55 display: display_for_key_code(&key),
56 key,
57 modifiers: None,
58 description: description.to_string(),
59 visible: true,
60 });
61 self
62 }
63
64 pub fn bind_mod(mut self, key: char, mods: KeyModifiers, description: &str) -> Self {
66 self.bindings.push(Binding {
67 key: KeyCode::Char(key),
68 modifiers: Some(mods),
69 display: display_for_mod_char(mods, key),
70 description: description.to_string(),
71 visible: true,
72 });
73 self
74 }
75
76 pub fn bind_hidden(mut self, key: char, description: &str) -> Self {
78 self.bindings.push(Binding {
79 key: KeyCode::Char(key),
80 modifiers: None,
81 display: key.to_string(),
82 description: description.to_string(),
83 visible: false,
84 });
85 self
86 }
87
88 pub fn visible_bindings(&self) -> impl Iterator<Item = &Binding> {
90 self.bindings.iter().filter(|binding| binding.visible)
91 }
92}
93
94fn display_for_key_code(key: &KeyCode) -> String {
95 match key {
96 KeyCode::Char(c) => c.to_string(),
97 KeyCode::Enter => "Enter".to_string(),
98 KeyCode::Backspace => "Backspace".to_string(),
99 KeyCode::Tab => "Tab".to_string(),
100 KeyCode::BackTab => "Shift+Tab".to_string(),
101 KeyCode::Esc => "Esc".to_string(),
102 KeyCode::Up => "↑".to_string(),
103 KeyCode::Down => "↓".to_string(),
104 KeyCode::Left => "←".to_string(),
105 KeyCode::Right => "→".to_string(),
106 KeyCode::Home => "Home".to_string(),
107 KeyCode::End => "End".to_string(),
108 KeyCode::PageUp => "PgUp".to_string(),
109 KeyCode::PageDown => "PgDn".to_string(),
110 KeyCode::Delete => "Del".to_string(),
111 KeyCode::F(n) => format!("F{n}"),
112 }
113}
114
115fn display_for_mod_char(mods: KeyModifiers, key: char) -> String {
116 let mut parts: Vec<&str> = Vec::new();
117 if mods.contains(KeyModifiers::CONTROL) {
118 parts.push("Ctrl");
119 }
120 if mods.contains(KeyModifiers::ALT) {
121 parts.push("Alt");
122 }
123 if mods.contains(KeyModifiers::SHIFT) {
124 parts.push("Shift");
125 }
126
127 if parts.is_empty() {
128 key.to_string()
129 } else {
130 format!("{}+{}", parts.join("+"), key.to_ascii_uppercase())
131 }
132}