keybinds/
crossterm.rs

1//! Support for [`crossterm`] crate.
2//!
3//! This module provides the conversions from crossterm's event types to [`Key`], [`Mods`],
4//! and [`KeyInput`].
5//!
6//! ```no_run
7//! use crossterm::event::{read, Event};
8//! use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
9//! use keybinds::{KeyInput, KeybindDispatcher};
10//! use std::io;
11//!
12//! #[derive(PartialEq, Eq, Debug)]
13//! enum Action {
14//!     SayHi,
15//!     Exit,
16//! }
17//!
18//! let mut dispatcher = KeybindDispatcher::default();
19//! dispatcher.bind("h i", Action::SayHi).unwrap();
20//! dispatcher.bind("Ctrl+x Ctrl+c", Action::Exit).unwrap();
21//!
22//! enable_raw_mode().unwrap();
23//!
24//! while let Ok(event) = read() {
25//!     if let Event::Key(event) = event {
26//!         // Convert crossterm's `KeyEvent` into `KeyInput`
27//!         println!("Key input `{:?}`\r", KeyInput::from(event));
28//!
29//!         // `KeybindDispatcher::dispatch` accepts crossterm's `KeyEvent`
30//!         if let Some(action) = dispatcher.dispatch(event) {
31//!             match action {
32//!                 Action::SayHi => println!("Hi!"),
33//!                 Action::Exit => break,
34//!             }
35//!         }
36//!     }
37//! }
38//!
39//! disable_raw_mode().unwrap();
40//! ```
41use crate::{Key, KeyInput, Mods};
42use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers, MediaKeyCode};
43
44impl From<KeyCode> for Key {
45    fn from(code: KeyCode) -> Self {
46        match code {
47            KeyCode::Backspace => Self::Backspace,
48            KeyCode::Enter => Self::Enter,
49            KeyCode::Left => Self::Left,
50            KeyCode::Right => Self::Right,
51            KeyCode::Up => Self::Up,
52            KeyCode::Down => Self::Down,
53            KeyCode::Home => Self::Home,
54            KeyCode::End => Self::End,
55            KeyCode::PageUp => Self::PageUp,
56            KeyCode::PageDown => Self::PageDown,
57            KeyCode::Tab => Self::Tab,
58            KeyCode::BackTab => Self::Backtab,
59            KeyCode::Delete => Self::Delete,
60            KeyCode::Insert => Self::Insert,
61            KeyCode::F(i) => Self::F(i),
62            KeyCode::Char(c) => Self::Char(c),
63            KeyCode::Esc => Self::Esc,
64            KeyCode::ScrollLock => Self::ScrollLock,
65            KeyCode::NumLock => Self::NumLock,
66            KeyCode::PrintScreen => Self::PrintScreen,
67            KeyCode::Menu => Self::Menu,
68            KeyCode::Media(MediaKeyCode::Play) => Self::Play,
69            KeyCode::Media(MediaKeyCode::Pause) => Self::Pause,
70            KeyCode::Media(MediaKeyCode::PlayPause) => Self::PlayPause,
71            KeyCode::Media(MediaKeyCode::Stop) => Self::Stop,
72            KeyCode::Media(MediaKeyCode::Rewind) => Self::Rewind,
73            KeyCode::Media(MediaKeyCode::TrackNext) => Self::NextTrack,
74            KeyCode::Media(MediaKeyCode::TrackPrevious) => Self::PrevTrack,
75            KeyCode::Media(MediaKeyCode::LowerVolume) => Self::VolumeDown,
76            KeyCode::Media(MediaKeyCode::RaiseVolume) => Self::VolumeUp,
77            KeyCode::Media(MediaKeyCode::MuteVolume) => Self::Mute,
78            KeyCode::Modifier(_) | KeyCode::Null => Self::Ignored,
79            _ => Self::Unidentified,
80        }
81    }
82}
83
84impl From<KeyModifiers> for Mods {
85    fn from(from: KeyModifiers) -> Self {
86        let mut to = Mods::NONE;
87        if from.contains(KeyModifiers::CONTROL) {
88            to |= Mods::CTRL;
89        }
90        if from.contains(KeyModifiers::ALT) {
91            to |= Mods::ALT;
92        }
93        if from.contains(KeyModifiers::SUPER) {
94            to |= Mods::SUPER;
95        }
96        if from.contains(KeyModifiers::META) {
97            to |= Mods::CMD;
98        }
99        to
100    }
101}
102
103impl From<&KeyEvent> for KeyInput {
104    fn from(event: &KeyEvent) -> Self {
105        let key: Key = event.code.into();
106        let mods = event.modifiers.into();
107        Self::new(key, mods)
108    }
109}
110
111impl From<KeyEvent> for KeyInput {
112    fn from(event: KeyEvent) -> Self {
113        Self::from(&event)
114    }
115}
116
117impl From<&Event> for KeyInput {
118    fn from(event: &Event) -> Self {
119        match event {
120            Event::Key(event) => event.into(),
121            _ => Key::Ignored.into(),
122        }
123    }
124}
125
126impl From<Event> for KeyInput {
127    fn from(event: Event) -> Self {
128        Self::from(&event)
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135    use crossterm::event::{KeyEventKind, KeyEventState, ModifierKeyCode};
136
137    #[test]
138    fn convert_key_code() {
139        assert_eq!(Key::from(KeyCode::Backspace), Key::Backspace);
140        assert_eq!(Key::from(KeyCode::Char('a')), Key::Char('a'));
141        assert_eq!(Key::from(KeyCode::Char('A')), Key::Char('A'));
142        assert_eq!(Key::from(KeyCode::KeypadBegin), Key::Unidentified);
143        assert_eq!(Key::from(KeyCode::Null), Key::Ignored);
144        assert_eq!(
145            Key::from(KeyCode::Modifier(ModifierKeyCode::LeftControl)),
146            Key::Ignored,
147        );
148        assert_eq!(Key::from(KeyCode::Media(MediaKeyCode::Play)), Key::Play);
149        assert_eq!(Key::from(KeyCode::F(12)), Key::F(12));
150    }
151
152    #[test]
153    fn convert_modifiers() {
154        assert_eq!(Mods::from(KeyModifiers::NONE), Mods::NONE);
155        assert_eq!(
156            Mods::from(
157                KeyModifiers::CONTROL
158                    | KeyModifiers::SHIFT
159                    | KeyModifiers::ALT
160                    | KeyModifiers::META
161            ),
162            Mods::CTRL | Mods::ALT | Mods::CMD,
163        );
164        assert_eq!(Mods::from(KeyModifiers::SUPER), Mods::SUPER);
165    }
166
167    #[test]
168    fn convert_key_event() {
169        assert_eq!(
170            KeyInput::from(KeyEvent {
171                code: KeyCode::Char('A'),
172                modifiers: KeyModifiers::CONTROL | KeyModifiers::SHIFT,
173                kind: KeyEventKind::Press,
174                state: KeyEventState::NONE,
175            }),
176            KeyInput::new('A', Mods::CTRL),
177        );
178    }
179
180    #[test]
181    fn convert_event() {
182        assert_eq!(
183            KeyInput::from(Event::Key(KeyEvent {
184                code: KeyCode::Char('A'),
185                modifiers: KeyModifiers::CONTROL | KeyModifiers::SHIFT,
186                kind: KeyEventKind::Press,
187                state: KeyEventState::NONE,
188            })),
189            KeyInput::new('A', Mods::CTRL),
190        );
191        assert_eq!(
192            KeyInput::from(Event::FocusGained),
193            KeyInput::new(Key::Ignored, Mods::NONE),
194        );
195    }
196}