use crossterm::event::{KeyCode, KeyEventKind, KeyModifiers, MouseEventKind as CtMouseEventKind};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Event {
Key(KeyEvent),
Mouse(MouseEvent),
Resize { width: u16, height: u16 },
FocusGained,
FocusLost,
Paste(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct KeyEvent {
pub code: KeyCode,
pub modifiers: KeyModifiers,
}
impl KeyEvent {
pub fn is_char(&self, c: char) -> bool {
self.code == KeyCode::Char(c) && self.modifiers == KeyModifiers::NONE
}
pub fn is_ctrl(&self, c: char) -> bool {
self.code == KeyCode::Char(c) && self.modifiers.contains(KeyModifiers::CONTROL)
}
pub fn is_alt(&self, c: char) -> bool {
self.code == KeyCode::Char(c) && self.modifiers.contains(KeyModifiers::ALT)
}
pub fn is_key(&self, code: KeyCode) -> bool {
self.code == code
}
pub fn is_esc(&self) -> bool {
self.code == KeyCode::Esc
}
pub fn is_enter(&self) -> bool {
self.code == KeyCode::Enter
}
pub fn char(&self) -> Option<char> {
match self.code {
KeyCode::Char(c) => Some(c),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MouseEvent {
pub kind: MouseEventKind,
pub column: u16,
pub row: u16,
pub modifiers: KeyModifiers,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MouseEventKind {
Down(MouseButton),
Up(MouseButton),
Drag(MouseButton),
Moved,
ScrollDown,
ScrollUp,
ScrollLeft,
ScrollRight,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MouseButton {
Left,
Right,
Middle,
}
impl From<crossterm::event::Event> for Event {
fn from(event: crossterm::event::Event) -> Self {
match event {
crossterm::event::Event::Key(key) => Event::Key(KeyEvent {
code: key.code,
modifiers: key.modifiers,
}),
crossterm::event::Event::Mouse(mouse) => Event::Mouse(MouseEvent {
kind: convert_mouse_kind(mouse.kind),
column: mouse.column,
row: mouse.row,
modifiers: mouse.modifiers,
}),
crossterm::event::Event::Resize(w, h) => Event::Resize {
width: w,
height: h,
},
crossterm::event::Event::FocusGained => Event::FocusGained,
crossterm::event::Event::FocusLost => Event::FocusLost,
crossterm::event::Event::Paste(text) => Event::Paste(text),
}
}
}
impl Event {
pub fn from_crossterm(event: crossterm::event::Event) -> Option<Self> {
match event {
crossterm::event::Event::Key(key) if key.kind == KeyEventKind::Release => None,
event => Some(event.into()),
}
}
}
fn convert_mouse_button(button: crossterm::event::MouseButton) -> MouseButton {
match button {
crossterm::event::MouseButton::Left => MouseButton::Left,
crossterm::event::MouseButton::Right => MouseButton::Right,
crossterm::event::MouseButton::Middle => MouseButton::Middle,
}
}
fn convert_mouse_kind(kind: CtMouseEventKind) -> MouseEventKind {
match kind {
CtMouseEventKind::Down(b) => MouseEventKind::Down(convert_mouse_button(b)),
CtMouseEventKind::Up(b) => MouseEventKind::Up(convert_mouse_button(b)),
CtMouseEventKind::Drag(b) => MouseEventKind::Drag(convert_mouse_button(b)),
CtMouseEventKind::Moved => MouseEventKind::Moved,
CtMouseEventKind::ScrollDown => MouseEventKind::ScrollDown,
CtMouseEventKind::ScrollUp => MouseEventKind::ScrollUp,
CtMouseEventKind::ScrollLeft => MouseEventKind::ScrollLeft,
CtMouseEventKind::ScrollRight => MouseEventKind::ScrollRight,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn crossterm_key_release_is_ignored() {
let event = crossterm::event::Event::Key(crossterm::event::KeyEvent::new_with_kind(
KeyCode::Char('a'),
KeyModifiers::NONE,
KeyEventKind::Release,
));
assert_eq!(Event::from_crossterm(event), None);
}
#[test]
fn crossterm_key_press_and_repeat_are_kept() {
for kind in [KeyEventKind::Press, KeyEventKind::Repeat] {
let event = crossterm::event::Event::Key(crossterm::event::KeyEvent::new_with_kind(
KeyCode::Char('a'),
KeyModifiers::NONE,
kind,
));
assert_eq!(
Event::from_crossterm(event),
Some(Event::Key(KeyEvent {
code: KeyCode::Char('a'),
modifiers: KeyModifiers::NONE,
}))
);
}
}
#[test]
fn is_char() {
let e = KeyEvent {
code: KeyCode::Char('q'),
modifiers: KeyModifiers::NONE,
};
assert!(e.is_char('q'));
assert!(!e.is_char('x'));
}
#[test]
fn is_ctrl() {
let e = KeyEvent {
code: KeyCode::Char('c'),
modifiers: KeyModifiers::CONTROL,
};
assert!(e.is_ctrl('c'));
assert!(!e.is_ctrl('x'));
let plain = KeyEvent {
code: KeyCode::Char('c'),
modifiers: KeyModifiers::NONE,
};
assert!(!plain.is_ctrl('c'));
}
#[test]
fn is_alt() {
let e = KeyEvent {
code: KeyCode::Char('x'),
modifiers: KeyModifiers::ALT,
};
assert!(e.is_alt('x'));
}
#[test]
fn is_key() {
let e = KeyEvent {
code: KeyCode::Enter,
modifiers: KeyModifiers::NONE,
};
assert!(e.is_key(KeyCode::Enter));
assert!(e.is_enter());
assert!(!e.is_esc());
}
#[test]
fn char_extraction() {
let e = KeyEvent {
code: KeyCode::Char('a'),
modifiers: KeyModifiers::NONE,
};
assert_eq!(e.char(), Some('a'));
let esc = KeyEvent {
code: KeyCode::Esc,
modifiers: KeyModifiers::NONE,
};
assert_eq!(esc.char(), None);
}
}