use crossterm::event::{
Event as CrosstermEvent, KeyCode as CrosstermKeyCode, KeyEvent as CrosstermKeyEvent,
KeyEventKind, KeyModifiers as CrosstermKeyModifiers, MouseEvent as CrosstermMouseEvent,
};
use thiserror::Error;
use super::MouseEventKind;
use super::{Event, Key, KeyModifiers, KeyPressedEvent, MouseButton, MouseEvent};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Error)]
pub enum ConversionError {
#[error("Unknown crossterm event: {event:?}")]
UnknownEvent { event: CrosstermEvent },
#[error("Unknown crossterm key code: {key_code:?}")]
UnknownKey { key_code: CrosstermKeyCode },
#[error("Unknown crossterm modifiers: {modifiers:?}")]
UnknownModifiers { modifiers: u8 },
#[error("Key repeated event not supported")]
KeyReleasedEventNotSupported,
#[error("Key repeated event not supported")]
KeyRepeatedEventNotSupported,
}
impl TryFrom<CrosstermEvent> for Event {
type Error = ConversionError;
fn try_from(event: CrosstermEvent) -> Result<Self, Self::Error> {
use CrosstermEvent::*;
let event = match event {
Key(key_event) => Event::KeyPressed(key_event.try_into()?),
Mouse(mouse_event) => Event::Mouse(mouse_event.into()),
_ => return Err(ConversionError::UnknownEvent { event }),
};
Ok(event)
}
}
impl TryFrom<CrosstermKeyEvent> for KeyPressedEvent {
type Error = ConversionError;
fn try_from(key_event: CrosstermKeyEvent) -> Result<Self, Self::Error> {
match key_event.kind {
KeyEventKind::Press => Ok(KeyPressedEvent {
key: key_event.code.try_into()?,
modifiers: key_event.modifiers.into(),
}),
KeyEventKind::Release => Err(ConversionError::KeyReleasedEventNotSupported),
KeyEventKind::Repeat => Err(ConversionError::KeyRepeatedEventNotSupported),
}
}
}
impl TryFrom<CrosstermKeyCode> for Key {
type Error = ConversionError;
fn try_from(key_code: CrosstermKeyCode) -> Result<Self, Self::Error> {
use Key::*;
let key = match key_code {
CrosstermKeyCode::Char(c) => Char(c),
CrosstermKeyCode::Esc => Esc,
CrosstermKeyCode::Enter => Enter,
CrosstermKeyCode::Tab => Tab,
CrosstermKeyCode::BackTab => BackTab,
CrosstermKeyCode::Backspace => Backspace,
CrosstermKeyCode::Delete => Delete,
CrosstermKeyCode::Insert => Insert,
CrosstermKeyCode::Left => Left,
CrosstermKeyCode::Right => Right,
CrosstermKeyCode::Up => Up,
CrosstermKeyCode::Down => Down,
CrosstermKeyCode::Home => Home,
CrosstermKeyCode::End => End,
CrosstermKeyCode::PageUp => PageUp,
CrosstermKeyCode::PageDown => PageDown,
CrosstermKeyCode::F(num) => F(num),
CrosstermKeyCode::Null => Null,
key_code => return Err(ConversionError::UnknownKey { key_code }),
};
Ok(key)
}
}
impl From<CrosstermKeyModifiers> for KeyModifiers {
fn from(modifiers: CrosstermKeyModifiers) -> Self {
KeyModifiers::from_bits(modifiers.bits()).unwrap()
}
}
impl From<CrosstermMouseEvent> for MouseEvent {
fn from(mouse_event: CrosstermMouseEvent) -> Self {
MouseEvent {
column: mouse_event.column,
row: mouse_event.row,
kind: mouse_event.kind.into(),
modifiers: mouse_event.modifiers.into(),
}
}
}
use crossterm::event::MouseButton as CrosstermMouseButton;
use crossterm::event::MouseEventKind as CrosstermMouseEventKind;
impl From<CrosstermMouseButton> for MouseButton {
fn from(mouse_button: CrosstermMouseButton) -> Self {
match mouse_button {
CrosstermMouseButton::Left => MouseButton::Left,
CrosstermMouseButton::Right => MouseButton::Right,
CrosstermMouseButton::Middle => MouseButton::Middle,
}
}
}
impl From<CrosstermMouseEventKind> for MouseEventKind {
fn from(mouse_event_kind: CrosstermMouseEventKind) -> Self {
match mouse_event_kind {
CrosstermMouseEventKind::Down(button) => MouseEventKind::Down(button.into()),
CrosstermMouseEventKind::Up(button) => MouseEventKind::Up(button.into()),
CrosstermMouseEventKind::Drag(button) => MouseEventKind::Drag(button.into()),
CrosstermMouseEventKind::Moved => MouseEventKind::Moved,
CrosstermMouseEventKind::ScrollUp => MouseEventKind::ScrollUp,
CrosstermMouseEventKind::ScrollDown => MouseEventKind::ScrollDown,
CrosstermMouseEventKind::ScrollLeft => MouseEventKind::ScrollLeft,
CrosstermMouseEventKind::ScrollRight => MouseEventKind::ScrollRight,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::rstest;
#[rstest]
#[case(CrosstermKeyModifiers::empty(), KeyModifiers::empty())]
#[case(CrosstermKeyModifiers::NONE, KeyModifiers::empty())]
#[case(CrosstermKeyModifiers::SHIFT, KeyModifiers::SHIFT)]
#[case(CrosstermKeyModifiers::CONTROL, KeyModifiers::CTRL)]
#[case(CrosstermKeyModifiers::CONTROL, KeyModifiers::CONTROL)]
#[case(CrosstermKeyModifiers::ALT, KeyModifiers::ALT)]
#[case(CrosstermKeyModifiers::ALT, KeyModifiers::OPTION)]
#[case(CrosstermKeyModifiers::SUPER, KeyModifiers::SUPER)]
#[case(CrosstermKeyModifiers::SUPER, KeyModifiers::WIN)]
#[case(CrosstermKeyModifiers::SUPER, KeyModifiers::COMMAND)]
#[case(CrosstermKeyModifiers::HYPER, KeyModifiers::HYPER)]
#[case(CrosstermKeyModifiers::META, KeyModifiers::META)]
#[case(
CrosstermKeyModifiers::all(),
KeyModifiers::SHIFT | KeyModifiers::CTRL | KeyModifiers::ALT | KeyModifiers::SUPER |
KeyModifiers::HYPER | KeyModifiers::META,
)]
fn try_from_modifiers(
#[case] modifiers: CrosstermKeyModifiers,
#[case] expected: KeyModifiers,
) {
let result = KeyModifiers::from(modifiers);
assert_eq!(result, expected);
}
}