1#![forbid(unsafe_code)]
2#![warn(missing_docs)]
3#![doc = include_str!("../README.md")]
4
5#[cfg(feature = "widgets")]
7pub mod widgets;
8
9pub use ratatui_input_manager_derive::keymap;
10use std::fmt::Display;
11
12pub trait Backend {
14 type Key: 'static;
16 type Modifiers: 'static;
18 type Event;
20}
21
22#[cfg(feature = "crossterm")]
24pub struct CrosstermBackend;
25
26#[cfg(feature = "crossterm")]
27impl Backend for CrosstermBackend {
28 type Key = crossterm::event::KeyCode;
29 type Modifiers = crossterm::event::KeyModifiers;
30 type Event = crossterm::event::Event;
31}
32
33#[cfg(feature = "termion")]
35pub struct TermionBackend;
36
37#[cfg(feature = "termion")]
38impl Backend for TermionBackend {
39 type Key = termion::event::Key;
40 type Modifiers = ();
41 type Event = termion::event::Event;
42}
43
44#[cfg(feature = "termwiz")]
46pub struct TermwizBackend;
47
48#[cfg(feature = "termwiz")]
49impl Backend for TermwizBackend {
50 type Key = termwiz::input::KeyCode;
51 type Modifiers = termwiz::input::Modifiers;
52 type Event = termwiz::input::InputEvent;
53}
54
55pub trait KeyMap<B: Backend + 'static> {
57 const KEYBINDS: &'static [KeyBind<B>];
59
60 fn handle(&mut self, event: &B::Event) -> bool;
62}
63
64#[expect(missing_docs)]
66pub trait DynKeyMap<B: Backend + 'static> {
67 fn keybinds(&self) -> &'static [KeyBind<B>];
68
69 fn handle(&mut self, event: &B::Event) -> bool;
70}
71
72impl<B: Backend + 'static, T: KeyMap<B>> DynKeyMap<B> for T {
73 fn keybinds(&self) -> &'static [KeyBind<B>] {
74 T::KEYBINDS
75 }
76
77 fn handle(&mut self, event: &B::Event) -> bool {
78 self.handle(event)
79 }
80}
81
82pub struct KeyBind<B: Backend + 'static> {
84 pub pressed: &'static [KeyPress<B>],
86 pub description: &'static str,
88}
89
90pub struct KeyPress<B: Backend> {
92 pub key: B::Key,
94 pub modifiers: B::Modifiers,
96}
97
98#[cfg(feature = "crossterm")]
99impl Display for KeyPress<CrosstermBackend> {
100 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101 let modifiers = self.modifiers;
102 write!(f, "<")?;
103 if modifiers.contains(crossterm::event::KeyModifiers::CONTROL) {
104 write!(f, "C-")?;
105 }
106 if modifiers.contains(crossterm::event::KeyModifiers::ALT) {
107 write!(f, "M-")?;
108 }
109 if modifiers.contains(crossterm::event::KeyModifiers::SUPER) {
110 write!(f, "D-")?;
111 }
112 if modifiers.contains(crossterm::event::KeyModifiers::SHIFT) {
113 write!(f, "S-")?;
114 }
115 match self.key {
116 crossterm::event::KeyCode::Backspace => write!(f, "BS"),
117 crossterm::event::KeyCode::Enter => write!(f, "CR"),
118 crossterm::event::KeyCode::Left => write!(f, "Left"),
119 crossterm::event::KeyCode::Right => write!(f, "Right"),
120 crossterm::event::KeyCode::Up => write!(f, "Up"),
121 crossterm::event::KeyCode::Down => write!(f, "Down"),
122 crossterm::event::KeyCode::Home => write!(f, "Home"),
123 crossterm::event::KeyCode::End => write!(f, "End"),
124 crossterm::event::KeyCode::PageUp => write!(f, "PageUp"),
125 crossterm::event::KeyCode::PageDown => write!(f, "PageDown"),
126 crossterm::event::KeyCode::Tab => write!(f, "Tab"),
127 crossterm::event::KeyCode::BackTab => write!(f, "S-Tab"),
128 crossterm::event::KeyCode::Delete => write!(f, "Del"),
129 crossterm::event::KeyCode::Insert => write!(f, "Insert"),
130 crossterm::event::KeyCode::F(num) => write!(f, "F{num}"),
131 crossterm::event::KeyCode::Char(c) => write!(f, "{c}"),
132 crossterm::event::KeyCode::Null => write!(f, "Nul"),
133 crossterm::event::KeyCode::Esc => write!(f, "Esc"),
134 crossterm::event::KeyCode::CapsLock => write!(f, "CapsLock"),
135 crossterm::event::KeyCode::NumLock => write!(f, "NumLock"),
136 crossterm::event::KeyCode::ScrollLock => write!(f, "ScrollLock"),
137 crossterm::event::KeyCode::PrintScreen => write!(f, "PrintScreen"),
138 crossterm::event::KeyCode::Pause => write!(f, "Pause"),
139 crossterm::event::KeyCode::Menu => write!(f, "Menu"),
140 crossterm::event::KeyCode::KeypadBegin => write!(f, "kBegin"),
141 crossterm::event::KeyCode::Media(code) => match code {
142 crossterm::event::MediaKeyCode::Play => write!(f, "MediaPlay"),
143 crossterm::event::MediaKeyCode::Pause => write!(f, "MediaPause"),
144 crossterm::event::MediaKeyCode::PlayPause => write!(f, "MediaPlayPause"),
145 crossterm::event::MediaKeyCode::Reverse => write!(f, "MediaReverse"),
146 crossterm::event::MediaKeyCode::Stop => write!(f, "MediaStop"),
147 crossterm::event::MediaKeyCode::FastForward => write!(f, "MediaFastForward"),
148 crossterm::event::MediaKeyCode::Rewind => write!(f, "MediaRewind"),
149 crossterm::event::MediaKeyCode::TrackNext => write!(f, "MediaTrackNext"),
150 crossterm::event::MediaKeyCode::TrackPrevious => write!(f, "MediaTrackPrevious"),
151 crossterm::event::MediaKeyCode::Record => write!(f, "MediaRecord"),
152 crossterm::event::MediaKeyCode::LowerVolume => write!(f, "VolumeDown"),
153 crossterm::event::MediaKeyCode::RaiseVolume => write!(f, "VolumeUp"),
154 crossterm::event::MediaKeyCode::MuteVolume => write!(f, "VolumeMute"),
155 },
156 crossterm::event::KeyCode::Modifier(code) => match code {
157 crossterm::event::ModifierKeyCode::LeftShift => write!(f, "S-Left"),
158 crossterm::event::ModifierKeyCode::LeftControl => write!(f, "C-Left"),
159 crossterm::event::ModifierKeyCode::LeftAlt => write!(f, "M-Left"),
160 crossterm::event::ModifierKeyCode::LeftSuper => write!(f, "D-Left"),
161 crossterm::event::ModifierKeyCode::LeftHyper => write!(f, "H-Left"),
162 crossterm::event::ModifierKeyCode::LeftMeta => write!(f, "Meta-Left"),
163 crossterm::event::ModifierKeyCode::RightShift => write!(f, "S-Right"),
164 crossterm::event::ModifierKeyCode::RightControl => write!(f, "C-Right"),
165 crossterm::event::ModifierKeyCode::RightAlt => write!(f, "M-Right"),
166 crossterm::event::ModifierKeyCode::RightSuper => write!(f, "D-Right"),
167 crossterm::event::ModifierKeyCode::RightHyper => write!(f, "H-Right"),
168 crossterm::event::ModifierKeyCode::RightMeta => write!(f, "Meta-Right"),
169 crossterm::event::ModifierKeyCode::IsoLevel3Shift => {
170 write!(f, "ISO_Level3_Shift")
171 }
172 crossterm::event::ModifierKeyCode::IsoLevel5Shift => {
173 write!(f, "ISO_Level5_Shift")
174 }
175 },
176 }?;
177 write!(f, ">")
178 }
179}
180
181#[cfg(feature = "crossterm")]
182impl std::fmt::Debug for KeyPress<CrosstermBackend> {
183 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184 f.debug_struct("KeyPress")
185 .field("key", &self.key)
186 .field("modifiers", &self.modifiers)
187 .finish()
188 }
189}
190
191#[cfg(feature = "termion")]
192impl Display for KeyPress<TermionBackend> {
193 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
194 match self.key {
195 termion::event::Key::Backspace => write!(f, "<BS>"),
196 termion::event::Key::Left => write!(f, "<Left>"),
197 termion::event::Key::ShiftLeft => write!(f, "<S-Left>"),
198 termion::event::Key::AltLeft => write!(f, "<M-Left>"),
199 termion::event::Key::CtrlLeft => write!(f, "<C-Left>"),
200 termion::event::Key::Right => write!(f, "<Right>"),
201 termion::event::Key::ShiftRight => write!(f, "<S-Right>"),
202 termion::event::Key::AltRight => write!(f, "<M-Right>"),
203 termion::event::Key::CtrlRight => write!(f, "<C-Right>"),
204 termion::event::Key::Up => write!(f, "<Up>"),
205 termion::event::Key::ShiftUp => write!(f, "<S-Up>"),
206 termion::event::Key::AltUp => write!(f, "<M-Up>"),
207 termion::event::Key::CtrlUp => write!(f, "<C-Up>"),
208 termion::event::Key::Down => write!(f, "<Down>"),
209 termion::event::Key::ShiftDown => write!(f, "<S-Down>"),
210 termion::event::Key::AltDown => write!(f, "<M-Down>"),
211 termion::event::Key::CtrlDown => write!(f, "<C-Down>"),
212 termion::event::Key::Home => write!(f, "<Home>"),
213 termion::event::Key::CtrlHome => write!(f, "<C-Home>"),
214 termion::event::Key::End => write!(f, "<End>"),
215 termion::event::Key::CtrlEnd => write!(f, "<C-End>"),
216 termion::event::Key::PageUp => write!(f, "<PageUp>"),
217 termion::event::Key::PageDown => write!(f, "<PageDown>"),
218 termion::event::Key::BackTab => write!(f, "<S-Tab>"),
219 termion::event::Key::Delete => write!(f, "<Del>"),
220 termion::event::Key::Insert => write!(f, "<Insert>"),
221 termion::event::Key::F(num) => write!(f, "<F{num}>"),
222 termion::event::Key::Char(c) => write!(f, "{c}"),
223 termion::event::Key::Alt(c) => write!(f, "<M-{c}>"),
224 termion::event::Key::Ctrl(c) => write!(f, "<C-{c}>"),
225 termion::event::Key::Null => write!(f, "<Nul>"),
226 termion::event::Key::Esc => write!(f, "<Esc>"),
227 _ => write!(f, "<Unknown>"),
228 }
229 }
230}
231
232#[cfg(feature = "termion")]
233impl std::fmt::Debug for KeyPress<TermionBackend> {
234 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
235 f.debug_struct("KeyPress")
236 .field("key", &self.key)
237 .field("modifiers", &self.modifiers)
238 .finish()
239 }
240}
241
242#[cfg(feature = "termwiz")]
243impl Display for KeyPress<TermwizBackend> {
244 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
245 let modifiers = self.modifiers;
246 write!(f, "<")?;
247 if modifiers.contains(termwiz::input::Modifiers::CTRL) {
248 write!(f, "C-")?;
249 }
250 if modifiers.contains(termwiz::input::Modifiers::ALT) {
251 write!(f, "M-")?;
252 }
253 if modifiers.contains(termwiz::input::Modifiers::SUPER) {
254 write!(f, "D-")?;
255 }
256 if modifiers.contains(termwiz::input::Modifiers::SHIFT) {
257 write!(f, "S-")?;
258 }
259 match self.key {
260 termwiz::input::KeyCode::Char(c) => write!(f, "{c}"),
261 termwiz::input::KeyCode::Hyper => write!(f, "Hyper"),
262 termwiz::input::KeyCode::Super => write!(f, "Super"),
263 termwiz::input::KeyCode::Meta => write!(f, "M"),
264 termwiz::input::KeyCode::Cancel => write!(f, "Cancel"),
265 termwiz::input::KeyCode::Backspace => write!(f, "BS"),
266 termwiz::input::KeyCode::Tab => write!(f, "Tab"),
267 termwiz::input::KeyCode::Clear => write!(f, "Clear"),
268 termwiz::input::KeyCode::Enter => write!(f, "CR"),
269 termwiz::input::KeyCode::Shift => write!(f, "S"),
270 termwiz::input::KeyCode::Escape => write!(f, "Esc"),
271 termwiz::input::KeyCode::LeftShift => write!(f, "S-Left"),
272 termwiz::input::KeyCode::RightShift => write!(f, "S-Right"),
273 termwiz::input::KeyCode::Control => write!(f, "C"),
274 termwiz::input::KeyCode::LeftControl => write!(f, "C-Left"),
275 termwiz::input::KeyCode::RightControl => write!(f, "C-Right"),
276 termwiz::input::KeyCode::Alt => write!(f, "M"),
277 termwiz::input::KeyCode::LeftAlt => write!(f, "M-Left"),
278 termwiz::input::KeyCode::RightAlt => write!(f, "M-Right"),
279 termwiz::input::KeyCode::Menu => write!(f, "Menu"),
280 termwiz::input::KeyCode::LeftMenu => write!(f, "Menu-Left"),
281 termwiz::input::KeyCode::RightMenu => write!(f, "Menu-Right"),
282 termwiz::input::KeyCode::Pause => write!(f, "Pause"),
283 termwiz::input::KeyCode::CapsLock => write!(f, "CapsLock"),
284 termwiz::input::KeyCode::PageUp => write!(f, "PageUp"),
285 termwiz::input::KeyCode::PageDown => write!(f, "PageDown"),
286 termwiz::input::KeyCode::End => write!(f, "End"),
287 termwiz::input::KeyCode::Home => write!(f, "Home"),
288 termwiz::input::KeyCode::LeftArrow => write!(f, "Left"),
289 termwiz::input::KeyCode::RightArrow => write!(f, "Right"),
290 termwiz::input::KeyCode::UpArrow => write!(f, "Up"),
291 termwiz::input::KeyCode::DownArrow => write!(f, "Down"),
292 termwiz::input::KeyCode::Select => write!(f, "Select"),
293 termwiz::input::KeyCode::Print => write!(f, "Print"),
294 termwiz::input::KeyCode::Execute => write!(f, "Execute"),
295 termwiz::input::KeyCode::PrintScreen => write!(f, "PrintScreen"),
296 termwiz::input::KeyCode::Insert => write!(f, "Insert"),
297 termwiz::input::KeyCode::Delete => write!(f, "Del"),
298 termwiz::input::KeyCode::Help => write!(f, "Help"),
299 termwiz::input::KeyCode::LeftWindows => write!(f, "Windows-Left"),
300 termwiz::input::KeyCode::RightWindows => write!(f, "Windows-Right"),
301 termwiz::input::KeyCode::Applications => write!(f, "Apps"),
302 termwiz::input::KeyCode::Sleep => write!(f, "Sleep"),
303 termwiz::input::KeyCode::Numpad0 => write!(f, "k0"),
304 termwiz::input::KeyCode::Numpad1 => write!(f, "k1"),
305 termwiz::input::KeyCode::Numpad2 => write!(f, "k2"),
306 termwiz::input::KeyCode::Numpad3 => write!(f, "k3"),
307 termwiz::input::KeyCode::Numpad4 => write!(f, "k4"),
308 termwiz::input::KeyCode::Numpad5 => write!(f, "k5"),
309 termwiz::input::KeyCode::Numpad6 => write!(f, "k6"),
310 termwiz::input::KeyCode::Numpad7 => write!(f, "k7"),
311 termwiz::input::KeyCode::Numpad8 => write!(f, "k8"),
312 termwiz::input::KeyCode::Numpad9 => write!(f, "k9"),
313 termwiz::input::KeyCode::Multiply => write!(f, "kMultiply"),
314 termwiz::input::KeyCode::Add => write!(f, "kAdd"),
315 termwiz::input::KeyCode::Separator => write!(f, "kSeparator"),
316 termwiz::input::KeyCode::Subtract => write!(f, "kSubtract"),
317 termwiz::input::KeyCode::Decimal => write!(f, "kDecimal"),
318 termwiz::input::KeyCode::Divide => write!(f, "kDivide"),
319 termwiz::input::KeyCode::Function(n) => write!(f, "F{n}"),
320 termwiz::input::KeyCode::NumLock => write!(f, "NumLock"),
321 termwiz::input::KeyCode::ScrollLock => write!(f, "ScrollLock"),
322 termwiz::input::KeyCode::Copy => write!(f, "Copy"),
323 termwiz::input::KeyCode::Cut => write!(f, "Cut"),
324 termwiz::input::KeyCode::Paste => write!(f, "Paste"),
325 termwiz::input::KeyCode::BrowserBack => write!(f, "BrowserBack"),
326 termwiz::input::KeyCode::BrowserForward => write!(f, "BrowserForward"),
327 termwiz::input::KeyCode::BrowserRefresh => write!(f, "BrowserRefresh"),
328 termwiz::input::KeyCode::BrowserStop => write!(f, "BrowserStop"),
329 termwiz::input::KeyCode::BrowserSearch => write!(f, "BrowserSearch"),
330 termwiz::input::KeyCode::BrowserFavorites => write!(f, "BrowserFavorites"),
331 termwiz::input::KeyCode::BrowserHome => write!(f, "BrowserHome"),
332 termwiz::input::KeyCode::VolumeMute => write!(f, "VolumeMute"),
333 termwiz::input::KeyCode::VolumeDown => write!(f, "VolumeDown"),
334 termwiz::input::KeyCode::VolumeUp => write!(f, "VolumeUp"),
335 termwiz::input::KeyCode::MediaNextTrack => write!(f, "MediaNextTrack"),
336 termwiz::input::KeyCode::MediaPrevTrack => write!(f, "MediaPrevTrack"),
337 termwiz::input::KeyCode::MediaStop => write!(f, "MediaStop"),
338 termwiz::input::KeyCode::MediaPlayPause => write!(f, "MediaPlayPause"),
339 termwiz::input::KeyCode::ApplicationLeftArrow => write!(f, "App-Left"),
340 termwiz::input::KeyCode::ApplicationRightArrow => write!(f, "App-Right"),
341 termwiz::input::KeyCode::ApplicationUpArrow => write!(f, "App-Up"),
342 termwiz::input::KeyCode::ApplicationDownArrow => write!(f, "App-Down"),
343 termwiz::input::KeyCode::KeyPadHome => write!(f, "kHome"),
344 termwiz::input::KeyCode::KeyPadEnd => write!(f, "kEnd"),
345 termwiz::input::KeyCode::KeyPadPageUp => write!(f, "kPageUp"),
346 termwiz::input::KeyCode::KeyPadPageDown => write!(f, "kPageDown"),
347 termwiz::input::KeyCode::KeyPadBegin => write!(f, "kBegin"),
348 _ => write!(f, "Unknown"),
349 }?;
350 write!(f, ">")
351 }
352}