use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct KeyEvent {
pub key: Key,
pub modifiers: KeyModifiers,
}
impl KeyEvent {
pub fn new(key: Key, modifiers: KeyModifiers) -> Self {
Self { key, modifiers }
}
pub fn key(key: Key) -> Self {
Self::new(key, KeyModifiers::NONE)
}
pub fn char(c: char) -> Self {
Self::key(Key::Char(c))
}
pub fn ctrl(&self) -> bool {
self.modifiers.ctrl
}
pub fn alt(&self) -> bool {
self.modifiers.alt
}
pub fn shift(&self) -> bool {
self.modifiers.shift
}
pub fn is_ctrl_c(&self) -> bool {
self.ctrl() && self.key == Key::Char('c')
}
pub fn is_escape(&self) -> bool {
self.key == Key::Esc
}
pub fn is_enter(&self) -> bool {
self.key == Key::Enter
}
pub fn is_backspace(&self) -> bool {
self.key == Key::Backspace
}
pub fn is_printable(&self) -> bool {
matches!(self.key, Key::Char(c) if !c.is_control())
}
}
impl From<crossterm::event::KeyEvent> for KeyEvent {
fn from(event: crossterm::event::KeyEvent) -> Self {
Self {
key: event.code.into(),
modifiers: event.modifiers.into(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Key {
Char(char),
F(u8),
Backspace,
Enter,
Left,
Right,
Up,
Down,
Home,
End,
PageUp,
PageDown,
Tab,
BackTab,
Delete,
Insert,
Esc,
CapsLock,
ScrollLock,
NumLock,
PrintScreen,
Pause,
Menu,
Null,
}
impl From<crossterm::event::KeyCode> for Key {
fn from(code: crossterm::event::KeyCode) -> Self {
use crossterm::event::KeyCode;
match code {
KeyCode::Char(c) => Key::Char(c),
KeyCode::F(n) => Key::F(n),
KeyCode::Backspace => Key::Backspace,
KeyCode::Enter => Key::Enter,
KeyCode::Left => Key::Left,
KeyCode::Right => Key::Right,
KeyCode::Up => Key::Up,
KeyCode::Down => Key::Down,
KeyCode::Home => Key::Home,
KeyCode::End => Key::End,
KeyCode::PageUp => Key::PageUp,
KeyCode::PageDown => Key::PageDown,
KeyCode::Tab => Key::Tab,
KeyCode::BackTab => Key::BackTab,
KeyCode::Delete => Key::Delete,
KeyCode::Insert => Key::Insert,
KeyCode::Esc => Key::Esc,
KeyCode::CapsLock => Key::CapsLock,
KeyCode::ScrollLock => Key::ScrollLock,
KeyCode::NumLock => Key::NumLock,
KeyCode::PrintScreen => Key::PrintScreen,
KeyCode::Pause => Key::Pause,
KeyCode::Menu => Key::Menu,
KeyCode::Null => Key::Null,
_ => Key::Null,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub struct KeyModifiers {
pub shift: bool,
pub ctrl: bool,
pub alt: bool,
pub super_key: bool,
pub hyper: bool,
pub meta: bool,
}
impl KeyModifiers {
pub const NONE: Self = Self {
shift: false,
ctrl: false,
alt: false,
super_key: false,
hyper: false,
meta: false,
};
pub fn is_empty(&self) -> bool {
!self.shift && !self.ctrl && !self.alt && !self.super_key && !self.hyper && !self.meta
}
}
impl From<crossterm::event::KeyModifiers> for KeyModifiers {
fn from(mods: crossterm::event::KeyModifiers) -> Self {
Self {
shift: mods.contains(crossterm::event::KeyModifiers::SHIFT),
ctrl: mods.contains(crossterm::event::KeyModifiers::CONTROL),
alt: mods.contains(crossterm::event::KeyModifiers::ALT),
super_key: mods.contains(crossterm::event::KeyModifiers::SUPER),
hyper: mods.contains(crossterm::event::KeyModifiers::HYPER),
meta: mods.contains(crossterm::event::KeyModifiers::META),
}
}
}
#[cfg(test)]
mod tests {
use super::{Key, KeyEvent, KeyModifiers};
#[test]
fn test_key_event_new() {
let mods = KeyModifiers {
ctrl: true,
..KeyModifiers::NONE
};
let event = KeyEvent::new(Key::Char('a'), mods);
assert_eq!(event.key, Key::Char('a'));
assert!(event.ctrl());
}
#[test]
fn test_key_event_char() {
let event = KeyEvent::char('x');
assert_eq!(event.key, Key::Char('x'));
assert!(event.modifiers.is_empty());
}
#[test]
fn test_is_ctrl_c() {
let mods = KeyModifiers {
ctrl: true,
..KeyModifiers::NONE
};
let event = KeyEvent::new(Key::Char('c'), mods);
assert!(event.is_ctrl_c());
let event = KeyEvent::char('c');
assert!(!event.is_ctrl_c());
}
#[test]
fn test_is_printable() {
let event = KeyEvent::char('a');
assert!(event.is_printable());
let event = KeyEvent::key(Key::Enter);
assert!(!event.is_printable());
}
#[test]
fn test_modifiers_empty() {
let mods = KeyModifiers::NONE;
assert!(mods.is_empty());
let mods = KeyModifiers {
ctrl: true,
..KeyModifiers::NONE
};
assert!(!mods.is_empty());
}
}