1#[cfg(feature = "crossterm")]
11use crossterm::event as crossterm_event;
12
13#[derive(Debug, Clone, PartialEq, Eq)]
19pub enum Event {
20 Key(KeyEvent),
22 Mouse(MouseEvent),
24 Resize(u32, u32),
26 Paste(String),
28 FocusGained,
30 FocusLost,
32}
33
34impl Event {
35 pub fn key_char(c: char) -> Self {
37 Event::Key(KeyEvent {
38 code: KeyCode::Char(c),
39 modifiers: KeyModifiers::NONE,
40 kind: KeyEventKind::Press,
41 })
42 }
43
44 pub fn key(code: KeyCode) -> Self {
46 Event::Key(KeyEvent {
47 code,
48 modifiers: KeyModifiers::NONE,
49 kind: KeyEventKind::Press,
50 })
51 }
52
53 pub fn key_ctrl(c: char) -> Self {
55 Event::Key(KeyEvent {
56 code: KeyCode::Char(c),
57 modifiers: KeyModifiers::CONTROL,
58 kind: KeyEventKind::Press,
59 })
60 }
61
62 pub fn key_mod(code: KeyCode, modifiers: KeyModifiers) -> Self {
64 Event::Key(KeyEvent {
65 code,
66 modifiers,
67 kind: KeyEventKind::Press,
68 })
69 }
70
71 pub fn resize(width: u32, height: u32) -> Self {
73 Event::Resize(width, height)
74 }
75
76 pub fn mouse_click(x: u32, y: u32) -> Self {
78 Event::Mouse(MouseEvent {
79 kind: MouseKind::Down(MouseButton::Left),
80 x,
81 y,
82 modifiers: KeyModifiers::NONE,
83 })
84 }
85
86 pub fn mouse_move(x: u32, y: u32) -> Self {
88 Event::Mouse(MouseEvent {
89 kind: MouseKind::Moved,
90 x,
91 y,
92 modifiers: KeyModifiers::NONE,
93 })
94 }
95
96 pub fn paste(text: impl Into<String>) -> Self {
98 Event::Paste(text.into())
99 }
100}
101
102#[derive(Debug, Clone, PartialEq, Eq)]
104pub struct KeyEvent {
105 pub code: KeyCode,
107 pub modifiers: KeyModifiers,
109 pub kind: KeyEventKind,
111}
112
113#[derive(Debug, Clone, Copy, PartialEq, Eq)]
115pub enum KeyEventKind {
116 Press,
118 Release,
120 Repeat,
122}
123
124#[derive(Debug, Clone, PartialEq, Eq)]
130pub enum KeyCode {
131 Char(char),
133 Enter,
135 Backspace,
137 Tab,
139 BackTab,
141 Esc,
143 Up,
145 Down,
147 Left,
149 Right,
151 Home,
153 End,
155 PageUp,
157 PageDown,
159 Delete,
161 F(u8),
163}
164
165#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
170pub struct KeyModifiers(pub u8);
171
172impl KeyModifiers {
173 pub const NONE: Self = Self(0);
175 pub const SHIFT: Self = Self(1 << 0);
177 pub const CONTROL: Self = Self(1 << 1);
179 pub const ALT: Self = Self(1 << 2);
181
182 #[inline]
184 pub fn contains(self, other: Self) -> bool {
185 (self.0 & other.0) == other.0
186 }
187}
188
189#[derive(Debug, Clone, PartialEq, Eq)]
195pub struct MouseEvent {
196 pub kind: MouseKind,
198 pub x: u32,
200 pub y: u32,
202 pub modifiers: KeyModifiers,
204}
205
206#[derive(Debug, Clone, PartialEq, Eq)]
208pub enum MouseKind {
209 Down(MouseButton),
211 Up(MouseButton),
213 Drag(MouseButton),
215 ScrollUp,
217 ScrollDown,
219 Moved,
221}
222
223#[derive(Debug, Clone, Copy, PartialEq, Eq)]
225pub enum MouseButton {
226 Left,
228 Right,
230 Middle,
232}
233
234#[cfg(feature = "crossterm")]
235fn convert_modifiers(modifiers: crossterm_event::KeyModifiers) -> KeyModifiers {
236 let mut out = KeyModifiers::NONE;
237 if modifiers.contains(crossterm_event::KeyModifiers::SHIFT) {
238 out.0 |= KeyModifiers::SHIFT.0;
239 }
240 if modifiers.contains(crossterm_event::KeyModifiers::CONTROL) {
241 out.0 |= KeyModifiers::CONTROL.0;
242 }
243 if modifiers.contains(crossterm_event::KeyModifiers::ALT) {
244 out.0 |= KeyModifiers::ALT.0;
245 }
246 out
247}
248
249#[cfg(feature = "crossterm")]
250fn convert_button(button: crossterm_event::MouseButton) -> MouseButton {
251 match button {
252 crossterm_event::MouseButton::Left => MouseButton::Left,
253 crossterm_event::MouseButton::Right => MouseButton::Right,
254 crossterm_event::MouseButton::Middle => MouseButton::Middle,
255 }
256}
257
258#[cfg(feature = "crossterm")]
263pub(crate) fn from_crossterm(raw: crossterm_event::Event) -> Option<Event> {
264 match raw {
265 crossterm_event::Event::Key(k) => {
266 let code = match k.code {
267 crossterm_event::KeyCode::Char(c) => KeyCode::Char(c),
268 crossterm_event::KeyCode::Enter => KeyCode::Enter,
269 crossterm_event::KeyCode::Backspace => KeyCode::Backspace,
270 crossterm_event::KeyCode::Tab => KeyCode::Tab,
271 crossterm_event::KeyCode::BackTab => KeyCode::BackTab,
272 crossterm_event::KeyCode::Esc => KeyCode::Esc,
273 crossterm_event::KeyCode::Up => KeyCode::Up,
274 crossterm_event::KeyCode::Down => KeyCode::Down,
275 crossterm_event::KeyCode::Left => KeyCode::Left,
276 crossterm_event::KeyCode::Right => KeyCode::Right,
277 crossterm_event::KeyCode::Home => KeyCode::Home,
278 crossterm_event::KeyCode::End => KeyCode::End,
279 crossterm_event::KeyCode::PageUp => KeyCode::PageUp,
280 crossterm_event::KeyCode::PageDown => KeyCode::PageDown,
281 crossterm_event::KeyCode::Delete => KeyCode::Delete,
282 crossterm_event::KeyCode::F(n) => KeyCode::F(n),
283 _ => return None,
284 };
285 let modifiers = convert_modifiers(k.modifiers);
286 let kind = match k.kind {
287 crossterm_event::KeyEventKind::Press => KeyEventKind::Press,
288 crossterm_event::KeyEventKind::Repeat => KeyEventKind::Repeat,
289 crossterm_event::KeyEventKind::Release => KeyEventKind::Release,
290 };
291 Some(Event::Key(KeyEvent {
292 code,
293 modifiers,
294 kind,
295 }))
296 }
297 crossterm_event::Event::Mouse(m) => {
298 let kind = match m.kind {
299 crossterm_event::MouseEventKind::Down(btn) => MouseKind::Down(convert_button(btn)),
300 crossterm_event::MouseEventKind::Up(btn) => MouseKind::Up(convert_button(btn)),
301 crossterm_event::MouseEventKind::Drag(btn) => MouseKind::Drag(convert_button(btn)),
302 crossterm_event::MouseEventKind::Moved => MouseKind::Moved,
303 crossterm_event::MouseEventKind::ScrollUp => MouseKind::ScrollUp,
304 crossterm_event::MouseEventKind::ScrollDown => MouseKind::ScrollDown,
305 _ => return None,
306 };
307
308 Some(Event::Mouse(MouseEvent {
309 kind,
310 x: m.column as u32,
311 y: m.row as u32,
312 modifiers: convert_modifiers(m.modifiers),
313 }))
314 }
315 crossterm_event::Event::Resize(cols, rows) => Some(Event::Resize(cols as u32, rows as u32)),
316 crossterm_event::Event::Paste(s) => Some(Event::Paste(s)),
317 crossterm_event::Event::FocusGained => Some(Event::FocusGained),
318 crossterm_event::Event::FocusLost => Some(Event::FocusLost),
319 }
320}
321
322#[cfg(test)]
323mod event_constructor_tests {
324 use super::*;
325
326 #[test]
327 fn test_key_char() {
328 let e = Event::key_char('q');
329 if let Event::Key(k) = e {
330 assert!(matches!(k.code, KeyCode::Char('q')));
331 assert_eq!(k.modifiers, KeyModifiers::NONE);
332 assert!(matches!(k.kind, KeyEventKind::Press));
333 } else {
334 panic!("Expected Key event");
335 }
336 }
337
338 #[test]
339 fn test_key() {
340 let e = Event::key(KeyCode::Enter);
341 if let Event::Key(k) = e {
342 assert!(matches!(k.code, KeyCode::Enter));
343 assert_eq!(k.modifiers, KeyModifiers::NONE);
344 assert!(matches!(k.kind, KeyEventKind::Press));
345 } else {
346 panic!("Expected Key event");
347 }
348 }
349
350 #[test]
351 fn test_key_ctrl() {
352 let e = Event::key_ctrl('s');
353 if let Event::Key(k) = e {
354 assert!(matches!(k.code, KeyCode::Char('s')));
355 assert_eq!(k.modifiers, KeyModifiers::CONTROL);
356 assert!(matches!(k.kind, KeyEventKind::Press));
357 } else {
358 panic!("Expected Key event");
359 }
360 }
361
362 #[test]
363 fn test_key_mod() {
364 let modifiers = KeyModifiers(KeyModifiers::SHIFT.0 | KeyModifiers::ALT.0);
365 let e = Event::key_mod(KeyCode::Tab, modifiers);
366 if let Event::Key(k) = e {
367 assert!(matches!(k.code, KeyCode::Tab));
368 assert_eq!(k.modifiers, modifiers);
369 assert!(matches!(k.kind, KeyEventKind::Press));
370 } else {
371 panic!("Expected Key event");
372 }
373 }
374
375 #[test]
376 fn test_resize() {
377 let e = Event::resize(80, 24);
378 assert!(matches!(e, Event::Resize(80, 24)));
379 }
380
381 #[test]
382 fn test_mouse_click() {
383 let e = Event::mouse_click(10, 5);
384 if let Event::Mouse(m) = e {
385 assert!(matches!(m.kind, MouseKind::Down(MouseButton::Left)));
386 assert_eq!(m.x, 10);
387 assert_eq!(m.y, 5);
388 assert_eq!(m.modifiers, KeyModifiers::NONE);
389 } else {
390 panic!("Expected Mouse event");
391 }
392 }
393
394 #[test]
395 fn test_mouse_move() {
396 let e = Event::mouse_move(10, 5);
397 if let Event::Mouse(m) = e {
398 assert!(matches!(m.kind, MouseKind::Moved));
399 assert_eq!(m.x, 10);
400 assert_eq!(m.y, 5);
401 assert_eq!(m.modifiers, KeyModifiers::NONE);
402 } else {
403 panic!("Expected Mouse event");
404 }
405 }
406
407 #[test]
408 fn test_paste() {
409 let e = Event::paste("hello");
410 assert!(matches!(e, Event::Paste(s) if s == "hello"));
411 }
412}