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 pub fn as_key(&self) -> Option<&KeyEvent> {
103 match self {
104 Event::Key(k) => Some(k),
105 _ => None,
106 }
107 }
108
109 pub fn as_mouse(&self) -> Option<&MouseEvent> {
111 match self {
112 Event::Mouse(m) => Some(m),
113 _ => None,
114 }
115 }
116
117 pub fn as_resize(&self) -> Option<(u32, u32)> {
119 match self {
120 Event::Resize(w, h) => Some((*w, *h)),
121 _ => None,
122 }
123 }
124
125 pub fn as_paste(&self) -> Option<&str> {
127 match self {
128 Event::Paste(s) => Some(s),
129 _ => None,
130 }
131 }
132}
133
134#[derive(Debug, Clone, PartialEq, Eq)]
136pub struct KeyEvent {
137 pub code: KeyCode,
139 pub modifiers: KeyModifiers,
141 pub kind: KeyEventKind,
143}
144
145#[derive(Debug, Clone, Copy, PartialEq, Eq)]
147pub enum KeyEventKind {
148 Press,
150 Release,
152 Repeat,
154}
155
156#[derive(Debug, Clone, PartialEq, Eq)]
162pub enum KeyCode {
163 Char(char),
165 Enter,
167 Backspace,
169 Tab,
171 BackTab,
173 Esc,
175 Up,
177 Down,
179 Left,
181 Right,
183 Home,
185 End,
187 PageUp,
189 PageDown,
191 Delete,
193 F(u8),
195}
196
197#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
202pub struct KeyModifiers(pub u8);
203
204impl KeyModifiers {
205 pub const NONE: Self = Self(0);
207 pub const SHIFT: Self = Self(1 << 0);
209 pub const CONTROL: Self = Self(1 << 1);
211 pub const ALT: Self = Self(1 << 2);
213
214 #[inline]
216 pub fn contains(self, other: Self) -> bool {
217 (self.0 & other.0) == other.0
218 }
219}
220
221#[derive(Debug, Clone, PartialEq, Eq)]
227pub struct MouseEvent {
228 pub kind: MouseKind,
230 pub x: u32,
232 pub y: u32,
234 pub modifiers: KeyModifiers,
236}
237
238#[derive(Debug, Clone, PartialEq, Eq)]
240pub enum MouseKind {
241 Down(MouseButton),
243 Up(MouseButton),
245 Drag(MouseButton),
247 ScrollUp,
249 ScrollDown,
251 Moved,
253}
254
255#[derive(Debug, Clone, Copy, PartialEq, Eq)]
257pub enum MouseButton {
258 Left,
260 Right,
262 Middle,
264}
265
266#[cfg(feature = "crossterm")]
267fn convert_modifiers(modifiers: crossterm_event::KeyModifiers) -> KeyModifiers {
268 let mut out = KeyModifiers::NONE;
269 if modifiers.contains(crossterm_event::KeyModifiers::SHIFT) {
270 out.0 |= KeyModifiers::SHIFT.0;
271 }
272 if modifiers.contains(crossterm_event::KeyModifiers::CONTROL) {
273 out.0 |= KeyModifiers::CONTROL.0;
274 }
275 if modifiers.contains(crossterm_event::KeyModifiers::ALT) {
276 out.0 |= KeyModifiers::ALT.0;
277 }
278 out
279}
280
281#[cfg(feature = "crossterm")]
282fn convert_button(button: crossterm_event::MouseButton) -> MouseButton {
283 match button {
284 crossterm_event::MouseButton::Left => MouseButton::Left,
285 crossterm_event::MouseButton::Right => MouseButton::Right,
286 crossterm_event::MouseButton::Middle => MouseButton::Middle,
287 }
288}
289
290#[cfg(feature = "crossterm")]
295pub(crate) fn from_crossterm(raw: crossterm_event::Event) -> Option<Event> {
296 match raw {
297 crossterm_event::Event::Key(k) => {
298 let code = match k.code {
299 crossterm_event::KeyCode::Char(c) => KeyCode::Char(c),
300 crossterm_event::KeyCode::Enter => KeyCode::Enter,
301 crossterm_event::KeyCode::Backspace => KeyCode::Backspace,
302 crossterm_event::KeyCode::Tab => KeyCode::Tab,
303 crossterm_event::KeyCode::BackTab => KeyCode::BackTab,
304 crossterm_event::KeyCode::Esc => KeyCode::Esc,
305 crossterm_event::KeyCode::Up => KeyCode::Up,
306 crossterm_event::KeyCode::Down => KeyCode::Down,
307 crossterm_event::KeyCode::Left => KeyCode::Left,
308 crossterm_event::KeyCode::Right => KeyCode::Right,
309 crossterm_event::KeyCode::Home => KeyCode::Home,
310 crossterm_event::KeyCode::End => KeyCode::End,
311 crossterm_event::KeyCode::PageUp => KeyCode::PageUp,
312 crossterm_event::KeyCode::PageDown => KeyCode::PageDown,
313 crossterm_event::KeyCode::Delete => KeyCode::Delete,
314 crossterm_event::KeyCode::F(n) => KeyCode::F(n),
315 _ => return None,
316 };
317 let modifiers = convert_modifiers(k.modifiers);
318 let kind = match k.kind {
319 crossterm_event::KeyEventKind::Press => KeyEventKind::Press,
320 crossterm_event::KeyEventKind::Repeat => KeyEventKind::Repeat,
321 crossterm_event::KeyEventKind::Release => KeyEventKind::Release,
322 };
323 Some(Event::Key(KeyEvent {
324 code,
325 modifiers,
326 kind,
327 }))
328 }
329 crossterm_event::Event::Mouse(m) => {
330 let kind = match m.kind {
331 crossterm_event::MouseEventKind::Down(btn) => MouseKind::Down(convert_button(btn)),
332 crossterm_event::MouseEventKind::Up(btn) => MouseKind::Up(convert_button(btn)),
333 crossterm_event::MouseEventKind::Drag(btn) => MouseKind::Drag(convert_button(btn)),
334 crossterm_event::MouseEventKind::Moved => MouseKind::Moved,
335 crossterm_event::MouseEventKind::ScrollUp => MouseKind::ScrollUp,
336 crossterm_event::MouseEventKind::ScrollDown => MouseKind::ScrollDown,
337 _ => return None,
338 };
339
340 Some(Event::Mouse(MouseEvent {
341 kind,
342 x: m.column as u32,
343 y: m.row as u32,
344 modifiers: convert_modifiers(m.modifiers),
345 }))
346 }
347 crossterm_event::Event::Resize(cols, rows) => Some(Event::Resize(cols as u32, rows as u32)),
348 crossterm_event::Event::Paste(s) => Some(Event::Paste(s)),
349 crossterm_event::Event::FocusGained => Some(Event::FocusGained),
350 crossterm_event::Event::FocusLost => Some(Event::FocusLost),
351 }
352}
353
354#[cfg(test)]
355mod event_constructor_tests {
356 use super::*;
357
358 #[test]
359 fn test_key_char() {
360 let e = Event::key_char('q');
361 if let Event::Key(k) = e {
362 assert!(matches!(k.code, KeyCode::Char('q')));
363 assert_eq!(k.modifiers, KeyModifiers::NONE);
364 assert!(matches!(k.kind, KeyEventKind::Press));
365 } else {
366 panic!("Expected Key event");
367 }
368 }
369
370 #[test]
371 fn test_key() {
372 let e = Event::key(KeyCode::Enter);
373 if let Event::Key(k) = e {
374 assert!(matches!(k.code, KeyCode::Enter));
375 assert_eq!(k.modifiers, KeyModifiers::NONE);
376 assert!(matches!(k.kind, KeyEventKind::Press));
377 } else {
378 panic!("Expected Key event");
379 }
380 }
381
382 #[test]
383 fn test_key_ctrl() {
384 let e = Event::key_ctrl('s');
385 if let Event::Key(k) = e {
386 assert!(matches!(k.code, KeyCode::Char('s')));
387 assert_eq!(k.modifiers, KeyModifiers::CONTROL);
388 assert!(matches!(k.kind, KeyEventKind::Press));
389 } else {
390 panic!("Expected Key event");
391 }
392 }
393
394 #[test]
395 fn test_key_mod() {
396 let modifiers = KeyModifiers(KeyModifiers::SHIFT.0 | KeyModifiers::ALT.0);
397 let e = Event::key_mod(KeyCode::Tab, modifiers);
398 if let Event::Key(k) = e {
399 assert!(matches!(k.code, KeyCode::Tab));
400 assert_eq!(k.modifiers, modifiers);
401 assert!(matches!(k.kind, KeyEventKind::Press));
402 } else {
403 panic!("Expected Key event");
404 }
405 }
406
407 #[test]
408 fn test_resize() {
409 let e = Event::resize(80, 24);
410 assert!(matches!(e, Event::Resize(80, 24)));
411 }
412
413 #[test]
414 fn test_mouse_click() {
415 let e = Event::mouse_click(10, 5);
416 if let Event::Mouse(m) = e {
417 assert!(matches!(m.kind, MouseKind::Down(MouseButton::Left)));
418 assert_eq!(m.x, 10);
419 assert_eq!(m.y, 5);
420 assert_eq!(m.modifiers, KeyModifiers::NONE);
421 } else {
422 panic!("Expected Mouse event");
423 }
424 }
425
426 #[test]
427 fn test_mouse_move() {
428 let e = Event::mouse_move(10, 5);
429 if let Event::Mouse(m) = e {
430 assert!(matches!(m.kind, MouseKind::Moved));
431 assert_eq!(m.x, 10);
432 assert_eq!(m.y, 5);
433 assert_eq!(m.modifiers, KeyModifiers::NONE);
434 } else {
435 panic!("Expected Mouse event");
436 }
437 }
438
439 #[test]
440 fn test_paste() {
441 let e = Event::paste("hello");
442 assert!(matches!(e, Event::Paste(s) if s == "hello"));
443 }
444}