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>,
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_hidden(mut self, key: char, description: &str) -> Self {
80 self.bindings.push(Binding {
81 key: KeyCode::Char(key),
82 modifiers: None,
83 display: key.to_string(),
84 description: description.to_string(),
85 visible: false,
86 });
87 self
88 }
89
90 pub fn visible_bindings(&self) -> impl Iterator<Item = &Binding> {
92 self.bindings.iter().filter(|binding| binding.visible)
93 }
94}
95
96fn display_for_key_code(key: &KeyCode) -> String {
97 match key {
98 KeyCode::Char(c) => c.to_string(),
99 KeyCode::Enter => "Enter".to_string(),
100 KeyCode::Backspace => "Backspace".to_string(),
101 KeyCode::Tab => "Tab".to_string(),
102 KeyCode::BackTab => "Shift+Tab".to_string(),
103 KeyCode::Esc => "Esc".to_string(),
104 KeyCode::Up => "↑".to_string(),
105 KeyCode::Down => "↓".to_string(),
106 KeyCode::Left => "←".to_string(),
107 KeyCode::Right => "→".to_string(),
108 KeyCode::Home => "Home".to_string(),
109 KeyCode::End => "End".to_string(),
110 KeyCode::PageUp => "PgUp".to_string(),
111 KeyCode::PageDown => "PgDn".to_string(),
112 KeyCode::Delete => "Del".to_string(),
113 KeyCode::Insert => "Ins".to_string(),
114 KeyCode::Null => "Null".to_string(),
115 KeyCode::CapsLock => "CapsLock".to_string(),
116 KeyCode::ScrollLock => "ScrollLock".to_string(),
117 KeyCode::NumLock => "NumLock".to_string(),
118 KeyCode::PrintScreen => "PrtSc".to_string(),
119 KeyCode::Pause => "Pause".to_string(),
120 KeyCode::Menu => "Menu".to_string(),
121 KeyCode::KeypadBegin => "KP5".to_string(),
122 KeyCode::F(n) => format!("F{n}"),
123 }
124}
125
126fn display_for_mod_char(mods: KeyModifiers, key: char) -> String {
127 let mut parts: Vec<&str> = Vec::new();
128 if mods.contains(KeyModifiers::CONTROL) {
129 parts.push("Ctrl");
130 }
131 if mods.contains(KeyModifiers::ALT) {
132 parts.push("Alt");
133 }
134 if mods.contains(KeyModifiers::SHIFT) {
135 parts.push("Shift");
136 }
137 if mods.contains(KeyModifiers::SUPER) {
138 parts.push("Super");
139 }
140 if mods.contains(KeyModifiers::HYPER) {
141 parts.push("Hyper");
142 }
143 if mods.contains(KeyModifiers::META) {
144 parts.push("Meta");
145 }
146
147 if parts.is_empty() {
148 key.to_string()
149 } else {
150 format!("{}+{}", parts.join("+"), key.to_ascii_uppercase())
151 }
152}