ratatui_widgets/events/
crossterm.rs1use crossterm::event::{
2 Event as CrosstermEvent, KeyCode as CrosstermKeyCode, KeyEvent as CrosstermKeyEvent,
3 KeyEventKind, KeyModifiers as CrosstermKeyModifiers, MouseEvent as CrosstermMouseEvent,
4};
5use thiserror::Error;
6
7use super::MouseEventKind;
8use super::{Event, Key, KeyModifiers, KeyPressedEvent, MouseButton, MouseEvent};
9
10#[derive(Debug, Clone, PartialEq, Eq, Hash, Error)]
11pub enum ConversionError {
12 #[error("Unknown crossterm event: {event:?}")]
13 UnknownEvent { event: CrosstermEvent },
14 #[error("Unknown crossterm key code: {key_code:?}")]
15 UnknownKey { key_code: CrosstermKeyCode },
16 #[error("Unknown crossterm modifiers: {modifiers:?}")]
17 UnknownModifiers { modifiers: u8 },
18 #[error("Key repeated event not supported")]
19 KeyReleasedEventNotSupported,
20 #[error("Key repeated event not supported")]
21 KeyRepeatedEventNotSupported,
22}
23
24impl TryFrom<CrosstermEvent> for Event {
25 type Error = ConversionError;
26 fn try_from(event: CrosstermEvent) -> Result<Self, Self::Error> {
27 use CrosstermEvent::*;
28 let event = match event {
29 Key(key_event) => Event::KeyPressed(key_event.try_into()?),
30 Mouse(mouse_event) => Event::Mouse(mouse_event.into()),
31 _ => return Err(ConversionError::UnknownEvent { event }),
32 };
38 Ok(event)
39 }
40}
41
42impl TryFrom<CrosstermKeyEvent> for KeyPressedEvent {
43 type Error = ConversionError;
44 fn try_from(key_event: CrosstermKeyEvent) -> Result<Self, Self::Error> {
45 match key_event.kind {
46 KeyEventKind::Press => Ok(KeyPressedEvent {
47 key: key_event.code.try_into()?,
48 modifiers: key_event.modifiers.into(),
49 }),
50 KeyEventKind::Release => Err(ConversionError::KeyReleasedEventNotSupported),
51 KeyEventKind::Repeat => Err(ConversionError::KeyRepeatedEventNotSupported),
52 }
53 }
54}
55
56impl TryFrom<CrosstermKeyCode> for Key {
57 type Error = ConversionError;
58 fn try_from(key_code: CrosstermKeyCode) -> Result<Self, Self::Error> {
59 use Key::*;
60 let key = match key_code {
61 CrosstermKeyCode::Char(c) => Char(c),
62 CrosstermKeyCode::Esc => Esc,
63 CrosstermKeyCode::Enter => Enter,
64 CrosstermKeyCode::Tab => Tab,
65 CrosstermKeyCode::BackTab => BackTab,
66 CrosstermKeyCode::Backspace => Backspace,
67 CrosstermKeyCode::Delete => Delete,
68 CrosstermKeyCode::Insert => Insert,
69 CrosstermKeyCode::Left => Left,
70 CrosstermKeyCode::Right => Right,
71 CrosstermKeyCode::Up => Up,
72 CrosstermKeyCode::Down => Down,
73 CrosstermKeyCode::Home => Home,
74 CrosstermKeyCode::End => End,
75 CrosstermKeyCode::PageUp => PageUp,
76 CrosstermKeyCode::PageDown => PageDown,
77 CrosstermKeyCode::F(num) => F(num),
78 CrosstermKeyCode::Null => Null,
79
80 key_code => return Err(ConversionError::UnknownKey { key_code }),
81 };
92 Ok(key)
93 }
94}
95
96impl From<CrosstermKeyModifiers> for KeyModifiers {
97 fn from(modifiers: CrosstermKeyModifiers) -> Self {
98 KeyModifiers::from_bits(modifiers.bits()).unwrap()
101 }
102}
103
104impl From<CrosstermMouseEvent> for MouseEvent {
105 fn from(mouse_event: CrosstermMouseEvent) -> Self {
106 MouseEvent {
107 column: mouse_event.column,
108 row: mouse_event.row,
109 kind: mouse_event.kind.into(),
110 modifiers: mouse_event.modifiers.into(),
111 }
112 }
113}
114use crossterm::event::MouseButton as CrosstermMouseButton;
115use crossterm::event::MouseEventKind as CrosstermMouseEventKind;
116
117impl From<CrosstermMouseButton> for MouseButton {
118 fn from(mouse_button: CrosstermMouseButton) -> Self {
119 match mouse_button {
120 CrosstermMouseButton::Left => MouseButton::Left,
121 CrosstermMouseButton::Right => MouseButton::Right,
122 CrosstermMouseButton::Middle => MouseButton::Middle,
123 }
124 }
125}
126
127impl From<CrosstermMouseEventKind> for MouseEventKind {
128 fn from(mouse_event_kind: CrosstermMouseEventKind) -> Self {
129 match mouse_event_kind {
130 CrosstermMouseEventKind::Down(button) => MouseEventKind::Down(button.into()),
131 CrosstermMouseEventKind::Up(button) => MouseEventKind::Up(button.into()),
132 CrosstermMouseEventKind::Drag(button) => MouseEventKind::Drag(button.into()),
133 CrosstermMouseEventKind::Moved => MouseEventKind::Moved,
134 CrosstermMouseEventKind::ScrollUp => MouseEventKind::ScrollUp,
135 CrosstermMouseEventKind::ScrollDown => MouseEventKind::ScrollDown,
136 CrosstermMouseEventKind::ScrollLeft => MouseEventKind::ScrollLeft,
137 CrosstermMouseEventKind::ScrollRight => MouseEventKind::ScrollRight,
138 }
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145 use rstest::rstest;
146
147 #[rstest]
148 #[case(CrosstermKeyModifiers::empty(), KeyModifiers::empty())]
149 #[case(CrosstermKeyModifiers::NONE, KeyModifiers::empty())]
150 #[case(CrosstermKeyModifiers::SHIFT, KeyModifiers::SHIFT)]
151 #[case(CrosstermKeyModifiers::CONTROL, KeyModifiers::CTRL)]
152 #[case(CrosstermKeyModifiers::CONTROL, KeyModifiers::CONTROL)]
153 #[case(CrosstermKeyModifiers::ALT, KeyModifiers::ALT)]
154 #[case(CrosstermKeyModifiers::ALT, KeyModifiers::OPTION)]
155 #[case(CrosstermKeyModifiers::SUPER, KeyModifiers::SUPER)]
156 #[case(CrosstermKeyModifiers::SUPER, KeyModifiers::WIN)]
157 #[case(CrosstermKeyModifiers::SUPER, KeyModifiers::COMMAND)]
158 #[case(CrosstermKeyModifiers::HYPER, KeyModifiers::HYPER)]
159 #[case(CrosstermKeyModifiers::META, KeyModifiers::META)]
160 #[case(
161 CrosstermKeyModifiers::all(),
162 KeyModifiers::SHIFT | KeyModifiers::CTRL | KeyModifiers::ALT | KeyModifiers::SUPER |
163 KeyModifiers::HYPER | KeyModifiers::META,
164 )]
165 fn try_from_modifiers(
166 #[case] modifiers: CrosstermKeyModifiers,
167 #[case] expected: KeyModifiers,
168 ) {
169 let result = KeyModifiers::from(modifiers);
170 assert_eq!(result, expected);
171 }
172}