1#![forbid(unsafe_code)]
2
3use bitflags::bitflags;
17#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
18use crossterm::event as cte;
19
20#[derive(Debug, Clone, PartialEq, Eq)]
25pub enum Event {
26 Key(KeyEvent),
28
29 Mouse(MouseEvent),
31
32 Resize {
34 width: u16,
36 height: u16,
38 },
39
40 Paste(PasteEvent),
42
43 Focus(bool),
47
48 Clipboard(ClipboardEvent),
50
51 Tick,
57}
58
59impl Event {
60 #[must_use]
62 #[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
63 pub fn from_crossterm(event: cte::Event) -> Option<Self> {
64 map_crossterm_event_internal(event)
65 }
66
67 #[must_use]
69 pub const fn event_type_label(&self) -> &'static str {
70 match self {
71 Event::Key(_) => "key",
72 Event::Mouse(_) => "mouse",
73 Event::Resize { .. } => "resize",
74 Event::Paste(_) => "paste",
75 Event::Focus(_) => "focus",
76 Event::Clipboard(_) => "clipboard",
77 Event::Tick => "tick",
78 }
79 }
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
84pub struct KeyEvent {
85 pub code: KeyCode,
87
88 pub modifiers: Modifiers,
90
91 pub kind: KeyEventKind,
93}
94
95impl KeyEvent {
96 #[must_use]
98 pub const fn new(code: KeyCode) -> Self {
99 Self {
100 code,
101 modifiers: Modifiers::NONE,
102 kind: KeyEventKind::Press,
103 }
104 }
105
106 #[must_use]
108 pub const fn with_modifiers(mut self, modifiers: Modifiers) -> Self {
109 self.modifiers = modifiers;
110 self
111 }
112
113 #[must_use]
115 pub const fn with_kind(mut self, kind: KeyEventKind) -> Self {
116 self.kind = kind;
117 self
118 }
119
120 #[must_use]
122 pub fn is_char(&self, c: char) -> bool {
123 matches!(self.code, KeyCode::Char(ch) if ch == c)
124 }
125
126 #[must_use]
128 pub const fn ctrl(&self) -> bool {
129 self.modifiers.contains(Modifiers::CTRL)
130 }
131
132 #[must_use]
134 pub const fn alt(&self) -> bool {
135 self.modifiers.contains(Modifiers::ALT)
136 }
137
138 #[must_use]
140 pub const fn shift(&self) -> bool {
141 self.modifiers.contains(Modifiers::SHIFT)
142 }
143
144 #[must_use]
146 pub const fn super_key(&self) -> bool {
147 self.modifiers.contains(Modifiers::SUPER)
148 }
149}
150
151#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
153pub enum KeyCode {
154 Char(char),
156
157 Enter,
159
160 Escape,
162
163 Backspace,
165
166 Tab,
168
169 BackTab,
171
172 Delete,
174
175 Insert,
177
178 Home,
180
181 End,
183
184 PageUp,
186
187 PageDown,
189
190 Up,
192
193 Down,
195
196 Left,
198
199 Right,
201
202 F(u8),
204
205 Null,
207
208 MediaPlayPause,
210
211 MediaStop,
213
214 MediaNextTrack,
216
217 MediaPrevTrack,
219}
220
221#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
223pub enum KeyEventKind {
224 #[default]
226 Press,
227
228 Repeat,
230
231 Release,
233}
234
235bitflags! {
236 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
238 pub struct Modifiers: u8 {
239 const NONE = 0b0000;
241 const SHIFT = 0b0001;
243 const ALT = 0b0010;
245 const CTRL = 0b0100;
247 const SUPER = 0b1000;
249 }
250}
251
252impl Default for Modifiers {
253 fn default() -> Self {
254 Self::NONE
255 }
256}
257
258#[derive(Debug, Clone, Copy, PartialEq, Eq)]
260pub struct MouseEvent {
261 pub kind: MouseEventKind,
263
264 pub x: u16,
266
267 pub y: u16,
269
270 pub modifiers: Modifiers,
272}
273
274impl MouseEvent {
275 #[must_use]
277 pub const fn new(kind: MouseEventKind, x: u16, y: u16) -> Self {
278 Self {
279 kind,
280 x,
281 y,
282 modifiers: Modifiers::NONE,
283 }
284 }
285
286 #[must_use]
288 pub const fn with_modifiers(mut self, modifiers: Modifiers) -> Self {
289 self.modifiers = modifiers;
290 self
291 }
292
293 #[must_use]
295 pub const fn position(&self) -> (u16, u16) {
296 (self.x, self.y)
297 }
298}
299
300#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
302pub enum MouseEventKind {
303 Down(MouseButton),
305
306 Up(MouseButton),
308
309 Drag(MouseButton),
311
312 Moved,
314
315 ScrollUp,
317
318 ScrollDown,
320
321 ScrollLeft,
323
324 ScrollRight,
326}
327
328#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
330pub enum MouseButton {
331 Left,
333
334 Right,
336
337 Middle,
339}
340
341#[derive(Debug, Clone, PartialEq, Eq)]
343pub struct PasteEvent {
344 pub text: String,
346
347 pub bracketed: bool,
353}
354
355impl PasteEvent {
356 #[must_use]
358 pub fn new(text: impl Into<String>, bracketed: bool) -> Self {
359 Self {
360 text: text.into(),
361 bracketed,
362 }
363 }
364
365 #[must_use]
367 pub fn bracketed(text: impl Into<String>) -> Self {
368 Self::new(text, true)
369 }
370}
371
372#[derive(Debug, Clone, PartialEq, Eq)]
376pub struct ClipboardEvent {
377 pub content: String,
379
380 pub source: ClipboardSource,
382}
383
384impl ClipboardEvent {
385 #[must_use]
387 pub fn new(content: impl Into<String>, source: ClipboardSource) -> Self {
388 Self {
389 content: content.into(),
390 source,
391 }
392 }
393}
394
395#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
397pub enum ClipboardSource {
398 Osc52,
400
401 #[default]
403 Unknown,
404}
405
406#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
407fn map_crossterm_event_internal(event: cte::Event) -> Option<Event> {
408 match event {
409 cte::Event::Key(key) => map_key_event(key).map(Event::Key),
410 cte::Event::Mouse(mouse) => Some(Event::Mouse(map_mouse_event(mouse))),
411 cte::Event::Resize(width, height) => Some(Event::Resize { width, height }),
412 cte::Event::Paste(text) => Some(Event::Paste(PasteEvent::bracketed(text))),
413 cte::Event::FocusGained => Some(Event::Focus(true)),
414 cte::Event::FocusLost => Some(Event::Focus(false)),
415 }
416}
417
418#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
419fn map_key_event(event: cte::KeyEvent) -> Option<KeyEvent> {
420 let code = map_key_code(event.code)?;
421 let modifiers = map_modifiers(event.modifiers);
422 let kind = map_key_kind(event.kind);
423 Some(KeyEvent {
424 code,
425 modifiers,
426 kind,
427 })
428}
429
430#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
431fn map_key_kind(kind: cte::KeyEventKind) -> KeyEventKind {
432 match kind {
433 cte::KeyEventKind::Press => KeyEventKind::Press,
434 cte::KeyEventKind::Repeat => KeyEventKind::Repeat,
435 cte::KeyEventKind::Release => KeyEventKind::Release,
436 }
437}
438
439#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
440pub(crate) fn map_key_code(code: cte::KeyCode) -> Option<KeyCode> {
441 match code {
442 cte::KeyCode::Backspace => Some(KeyCode::Backspace),
443 cte::KeyCode::Enter => Some(KeyCode::Enter),
444 cte::KeyCode::Left => Some(KeyCode::Left),
445 cte::KeyCode::Right => Some(KeyCode::Right),
446 cte::KeyCode::Up => Some(KeyCode::Up),
447 cte::KeyCode::Down => Some(KeyCode::Down),
448 cte::KeyCode::Home => Some(KeyCode::Home),
449 cte::KeyCode::End => Some(KeyCode::End),
450 cte::KeyCode::PageUp => Some(KeyCode::PageUp),
451 cte::KeyCode::PageDown => Some(KeyCode::PageDown),
452 cte::KeyCode::Tab => Some(KeyCode::Tab),
453 cte::KeyCode::BackTab => Some(KeyCode::BackTab),
454 cte::KeyCode::Delete => Some(KeyCode::Delete),
455 cte::KeyCode::Insert => Some(KeyCode::Insert),
456 cte::KeyCode::F(n) => Some(KeyCode::F(n)),
457 cte::KeyCode::Char(c) => Some(KeyCode::Char(c)),
458 cte::KeyCode::Null => Some(KeyCode::Null),
459 cte::KeyCode::Esc => Some(KeyCode::Escape),
460 cte::KeyCode::Media(media) => map_media_key(media),
461 _ => None,
462 }
463}
464
465#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
466fn map_media_key(code: cte::MediaKeyCode) -> Option<KeyCode> {
467 match code {
468 cte::MediaKeyCode::Play | cte::MediaKeyCode::Pause | cte::MediaKeyCode::PlayPause => {
469 Some(KeyCode::MediaPlayPause)
470 }
471 cte::MediaKeyCode::Stop => Some(KeyCode::MediaStop),
472 cte::MediaKeyCode::TrackNext => Some(KeyCode::MediaNextTrack),
473 cte::MediaKeyCode::TrackPrevious => Some(KeyCode::MediaPrevTrack),
474 _ => None,
475 }
476}
477
478#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
479fn map_modifiers(modifiers: cte::KeyModifiers) -> Modifiers {
480 let mut mapped = Modifiers::NONE;
481 if modifiers.contains(cte::KeyModifiers::SHIFT) {
482 mapped |= Modifiers::SHIFT;
483 }
484 if modifiers.contains(cte::KeyModifiers::ALT) {
485 mapped |= Modifiers::ALT;
486 }
487 if modifiers.contains(cte::KeyModifiers::CONTROL) {
488 mapped |= Modifiers::CTRL;
489 }
490 if modifiers.contains(cte::KeyModifiers::SUPER)
491 || modifiers.contains(cte::KeyModifiers::HYPER)
492 || modifiers.contains(cte::KeyModifiers::META)
493 {
494 mapped |= Modifiers::SUPER;
495 }
496 mapped
497}
498
499#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
500fn map_mouse_event(event: cte::MouseEvent) -> MouseEvent {
501 let kind = match event.kind {
502 cte::MouseEventKind::Down(button) => MouseEventKind::Down(map_mouse_button(button)),
503 cte::MouseEventKind::Up(button) => MouseEventKind::Up(map_mouse_button(button)),
504 cte::MouseEventKind::Drag(button) => MouseEventKind::Drag(map_mouse_button(button)),
505 cte::MouseEventKind::Moved => MouseEventKind::Moved,
506 cte::MouseEventKind::ScrollUp => MouseEventKind::ScrollUp,
507 cte::MouseEventKind::ScrollDown => MouseEventKind::ScrollDown,
508 cte::MouseEventKind::ScrollLeft => MouseEventKind::ScrollLeft,
509 cte::MouseEventKind::ScrollRight => MouseEventKind::ScrollRight,
510 };
511
512 MouseEvent::new(kind, event.column, event.row).with_modifiers(map_modifiers(event.modifiers))
513}
514
515#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
516fn map_mouse_button(button: cte::MouseButton) -> MouseButton {
517 match button {
518 cte::MouseButton::Left => MouseButton::Left,
519 cte::MouseButton::Right => MouseButton::Right,
520 cte::MouseButton::Middle => MouseButton::Middle,
521 }
522}
523
524#[cfg(all(test, not(target_arch = "wasm32"), feature = "crossterm"))]
525mod tests {
526 use super::*;
527 use crossterm::event as ct_event;
528
529 #[test]
530 fn key_event_is_char() {
531 let event = KeyEvent::new(KeyCode::Char('q'));
532 assert!(event.is_char('q'));
533 assert!(!event.is_char('x'));
534 }
535
536 #[test]
537 fn key_event_modifiers() {
538 let event = KeyEvent::new(KeyCode::Char('c')).with_modifiers(Modifiers::CTRL);
539 assert!(event.ctrl());
540 assert!(!event.alt());
541 assert!(!event.shift());
542 assert!(!event.super_key());
543 }
544
545 #[test]
546 fn key_event_combined_modifiers() {
547 let event =
548 KeyEvent::new(KeyCode::Char('s')).with_modifiers(Modifiers::CTRL | Modifiers::SHIFT);
549 assert!(event.ctrl());
550 assert!(event.shift());
551 assert!(!event.alt());
552 }
553
554 #[test]
555 fn key_event_kind() {
556 let press = KeyEvent::new(KeyCode::Enter);
557 assert_eq!(press.kind, KeyEventKind::Press);
558
559 let release = press.with_kind(KeyEventKind::Release);
560 assert_eq!(release.kind, KeyEventKind::Release);
561 }
562
563 #[test]
564 fn mouse_event_position() {
565 let event = MouseEvent::new(MouseEventKind::Down(MouseButton::Left), 10, 20);
566 assert_eq!(event.position(), (10, 20));
567 assert_eq!(event.x, 10);
568 assert_eq!(event.y, 20);
569 }
570
571 #[test]
572 fn mouse_event_with_modifiers() {
573 let event = MouseEvent::new(MouseEventKind::Moved, 0, 0).with_modifiers(Modifiers::ALT);
574 assert_eq!(event.modifiers, Modifiers::ALT);
575 }
576
577 #[test]
578 fn paste_event_creation() {
579 let paste = PasteEvent::bracketed("hello world");
580 assert_eq!(paste.text, "hello world");
581 assert!(paste.bracketed);
582 }
583
584 #[test]
585 fn clipboard_event_creation() {
586 let clip = ClipboardEvent::new("copied text", ClipboardSource::Osc52);
587 assert_eq!(clip.content, "copied text");
588 assert_eq!(clip.source, ClipboardSource::Osc52);
589 }
590
591 #[test]
592 fn event_variants() {
593 let _key = Event::Key(KeyEvent::new(KeyCode::Char('a')));
595 let _mouse = Event::Mouse(MouseEvent::new(
596 MouseEventKind::Down(MouseButton::Left),
597 0,
598 0,
599 ));
600 let _resize = Event::Resize {
601 width: 80,
602 height: 24,
603 };
604 let _paste = Event::Paste(PasteEvent::bracketed("test"));
605 let _focus = Event::Focus(true);
606 let _clipboard = Event::Clipboard(ClipboardEvent::new("test", ClipboardSource::Unknown));
607 let _tick = Event::Tick;
608 }
609
610 #[test]
611 fn modifiers_default() {
612 assert_eq!(Modifiers::default(), Modifiers::NONE);
613 }
614
615 #[test]
616 fn key_event_kind_default() {
617 assert_eq!(KeyEventKind::default(), KeyEventKind::Press);
618 }
619
620 #[test]
621 fn clipboard_source_default() {
622 assert_eq!(ClipboardSource::default(), ClipboardSource::Unknown);
623 }
624
625 #[test]
626 fn function_keys() {
627 let f1 = KeyEvent::new(KeyCode::F(1));
628 let f12 = KeyEvent::new(KeyCode::F(12));
629 assert_eq!(f1.code, KeyCode::F(1));
630 assert_eq!(f12.code, KeyCode::F(12));
631 }
632
633 #[test]
634 fn event_is_clone_and_eq() {
635 let event = Event::Key(KeyEvent::new(KeyCode::Char('x')));
636 let cloned = event.clone();
637 assert_eq!(event, cloned);
638 }
639
640 #[test]
643 fn map_modifiers_ctrl() {
644 let mapped = map_modifiers(ct_event::KeyModifiers::CONTROL);
645 assert!(mapped.contains(Modifiers::CTRL));
646 assert!(!mapped.contains(Modifiers::SHIFT));
647 }
648
649 #[test]
650 fn map_modifiers_alt() {
651 let mapped = map_modifiers(ct_event::KeyModifiers::ALT);
652 assert!(mapped.contains(Modifiers::ALT));
653 }
654
655 #[test]
656 fn map_modifiers_super_variants() {
657 let super_mapped = map_modifiers(ct_event::KeyModifiers::SUPER);
658 assert!(super_mapped.contains(Modifiers::SUPER));
659
660 let hyper_mapped = map_modifiers(ct_event::KeyModifiers::HYPER);
661 assert!(hyper_mapped.contains(Modifiers::SUPER));
662
663 let meta_mapped = map_modifiers(ct_event::KeyModifiers::META);
664 assert!(meta_mapped.contains(Modifiers::SUPER));
665 }
666
667 #[test]
668 fn map_modifiers_combined() {
669 let combined = ct_event::KeyModifiers::SHIFT | ct_event::KeyModifiers::CONTROL;
670 let mapped = map_modifiers(combined);
671 assert!(mapped.contains(Modifiers::SHIFT));
672 assert!(mapped.contains(Modifiers::CTRL));
673 assert!(!mapped.contains(Modifiers::ALT));
674 }
675
676 #[test]
677 fn map_mouse_button_all() {
678 assert_eq!(
679 map_mouse_button(ct_event::MouseButton::Left),
680 MouseButton::Left
681 );
682 assert_eq!(
683 map_mouse_button(ct_event::MouseButton::Right),
684 MouseButton::Right
685 );
686 assert_eq!(
687 map_mouse_button(ct_event::MouseButton::Middle),
688 MouseButton::Middle
689 );
690 }
691
692 #[test]
693 fn map_mouse_event_down() {
694 let ct_event = ct_event::MouseEvent {
695 kind: ct_event::MouseEventKind::Down(ct_event::MouseButton::Left),
696 column: 10,
697 row: 5,
698 modifiers: ct_event::KeyModifiers::NONE,
699 };
700 let mapped = map_mouse_event(ct_event);
701 assert!(matches!(
702 mapped.kind,
703 MouseEventKind::Down(MouseButton::Left)
704 ));
705 assert_eq!(mapped.x, 10);
706 assert_eq!(mapped.y, 5);
707 }
708
709 #[test]
710 fn map_mouse_event_up() {
711 let ct_event = ct_event::MouseEvent {
712 kind: ct_event::MouseEventKind::Up(ct_event::MouseButton::Right),
713 column: 20,
714 row: 15,
715 modifiers: ct_event::KeyModifiers::NONE,
716 };
717 let mapped = map_mouse_event(ct_event);
718 assert!(matches!(
719 mapped.kind,
720 MouseEventKind::Up(MouseButton::Right)
721 ));
722 assert_eq!(mapped.x, 20);
723 assert_eq!(mapped.y, 15);
724 }
725
726 #[test]
727 fn map_mouse_event_drag() {
728 let ct_event = ct_event::MouseEvent {
729 kind: ct_event::MouseEventKind::Drag(ct_event::MouseButton::Middle),
730 column: 5,
731 row: 10,
732 modifiers: ct_event::KeyModifiers::NONE,
733 };
734 let mapped = map_mouse_event(ct_event);
735 assert!(matches!(
736 mapped.kind,
737 MouseEventKind::Drag(MouseButton::Middle)
738 ));
739 }
740
741 #[test]
742 fn map_mouse_event_moved() {
743 let ct_event = ct_event::MouseEvent {
744 kind: ct_event::MouseEventKind::Moved,
745 column: 0,
746 row: 0,
747 modifiers: ct_event::KeyModifiers::NONE,
748 };
749 let mapped = map_mouse_event(ct_event);
750 assert!(matches!(mapped.kind, MouseEventKind::Moved));
751 }
752
753 #[test]
754 fn map_mouse_event_scroll() {
755 let scroll_up = ct_event::MouseEvent {
756 kind: ct_event::MouseEventKind::ScrollUp,
757 column: 0,
758 row: 0,
759 modifiers: ct_event::KeyModifiers::NONE,
760 };
761 let scroll_down = ct_event::MouseEvent {
762 kind: ct_event::MouseEventKind::ScrollDown,
763 column: 0,
764 row: 0,
765 modifiers: ct_event::KeyModifiers::NONE,
766 };
767 let scroll_left = ct_event::MouseEvent {
768 kind: ct_event::MouseEventKind::ScrollLeft,
769 column: 0,
770 row: 0,
771 modifiers: ct_event::KeyModifiers::NONE,
772 };
773 let scroll_right = ct_event::MouseEvent {
774 kind: ct_event::MouseEventKind::ScrollRight,
775 column: 0,
776 row: 0,
777 modifiers: ct_event::KeyModifiers::NONE,
778 };
779
780 assert!(matches!(
781 map_mouse_event(scroll_up).kind,
782 MouseEventKind::ScrollUp
783 ));
784 assert!(matches!(
785 map_mouse_event(scroll_down).kind,
786 MouseEventKind::ScrollDown
787 ));
788 assert!(matches!(
789 map_mouse_event(scroll_left).kind,
790 MouseEventKind::ScrollLeft
791 ));
792 assert!(matches!(
793 map_mouse_event(scroll_right).kind,
794 MouseEventKind::ScrollRight
795 ));
796 }
797
798 #[test]
799 fn map_mouse_event_modifiers() {
800 let ct_event = ct_event::MouseEvent {
801 kind: ct_event::MouseEventKind::Down(ct_event::MouseButton::Left),
802 column: 0,
803 row: 0,
804 modifiers: ct_event::KeyModifiers::SHIFT | ct_event::KeyModifiers::ALT,
805 };
806 let mapped = map_mouse_event(ct_event);
807 assert!(mapped.modifiers.contains(Modifiers::SHIFT));
808 assert!(mapped.modifiers.contains(Modifiers::ALT));
809 }
810
811 #[test]
812 fn map_key_event_char() {
813 let ct_event = ct_event::KeyEvent {
814 code: ct_event::KeyCode::Char('x'),
815 modifiers: ct_event::KeyModifiers::CONTROL,
816 kind: ct_event::KeyEventKind::Press,
817 state: ct_event::KeyEventState::NONE,
818 };
819 let mapped = map_key_event(ct_event).expect("should map");
820 assert_eq!(mapped.code, KeyCode::Char('x'));
821 assert!(mapped.modifiers.contains(Modifiers::CTRL));
822 assert_eq!(mapped.kind, KeyEventKind::Press);
823 }
824
825 #[test]
826 fn map_key_event_function_key() {
827 let ct_event = ct_event::KeyEvent {
828 code: ct_event::KeyCode::F(5),
829 modifiers: ct_event::KeyModifiers::NONE,
830 kind: ct_event::KeyEventKind::Press,
831 state: ct_event::KeyEventState::NONE,
832 };
833 let mapped = map_key_event(ct_event).expect("should map");
834 assert_eq!(mapped.code, KeyCode::F(5));
835 }
836
837 #[test]
838 fn map_crossterm_event_key() {
839 let ct_event = ct_event::Event::Key(ct_event::KeyEvent {
840 code: ct_event::KeyCode::Enter,
841 modifiers: ct_event::KeyModifiers::NONE,
842 kind: ct_event::KeyEventKind::Press,
843 state: ct_event::KeyEventState::NONE,
844 });
845 let mapped = map_crossterm_event_internal(ct_event).expect("should map");
846 assert!(matches!(mapped, Event::Key(_)));
847 }
848
849 #[test]
850 fn map_crossterm_event_mouse() {
851 let ct_event = ct_event::Event::Mouse(ct_event::MouseEvent {
852 kind: ct_event::MouseEventKind::Down(ct_event::MouseButton::Left),
853 column: 10,
854 row: 5,
855 modifiers: ct_event::KeyModifiers::NONE,
856 });
857 let mapped = map_crossterm_event_internal(ct_event).expect("should map");
858 assert!(matches!(mapped, Event::Mouse(_)));
859 }
860
861 #[test]
862 fn map_crossterm_event_resize() {
863 let ct_event = ct_event::Event::Resize(80, 24);
864 let mapped = map_crossterm_event_internal(ct_event).expect("should map");
865 assert!(matches!(
866 mapped,
867 Event::Resize {
868 width: 80,
869 height: 24
870 }
871 ));
872 }
873
874 #[test]
875 fn map_crossterm_event_paste() {
876 let ct_event = ct_event::Event::Paste("hello world".to_string());
877 let mapped = map_crossterm_event_internal(ct_event).expect("should map");
878 match mapped {
879 Event::Paste(paste) => assert_eq!(paste.text, "hello world"),
880 _ => panic!("expected Paste event"),
881 }
882 }
883
884 #[test]
885 fn map_crossterm_event_focus() {
886 let gained = ct_event::Event::FocusGained;
887 let lost = ct_event::Event::FocusLost;
888
889 assert!(matches!(
890 map_crossterm_event_internal(gained),
891 Some(Event::Focus(true))
892 ));
893 assert!(matches!(
894 map_crossterm_event_internal(lost),
895 Some(Event::Focus(false))
896 ));
897 }
898
899 #[test]
900 fn map_key_kind_repeat_and_release() {
901 assert_eq!(
902 map_key_kind(ct_event::KeyEventKind::Repeat),
903 KeyEventKind::Repeat
904 );
905 assert_eq!(
906 map_key_kind(ct_event::KeyEventKind::Release),
907 KeyEventKind::Release
908 );
909 }
910
911 #[test]
912 fn map_key_code_escape() {
913 assert_eq!(map_key_code(ct_event::KeyCode::Esc), Some(KeyCode::Escape));
914 }
915
916 #[test]
917 fn map_key_code_unmapped_returns_none() {
918 assert_eq!(map_key_code(ct_event::KeyCode::CapsLock), None);
920 assert_eq!(map_key_code(ct_event::KeyCode::ScrollLock), None);
921 assert_eq!(map_key_code(ct_event::KeyCode::NumLock), None);
922 assert_eq!(map_key_code(ct_event::KeyCode::PrintScreen), None);
923 assert_eq!(map_key_code(ct_event::KeyCode::Pause), None);
924 assert_eq!(map_key_code(ct_event::KeyCode::Menu), None);
925 assert_eq!(map_key_code(ct_event::KeyCode::KeypadBegin), None);
926 assert_eq!(
927 map_key_code(ct_event::KeyCode::Modifier(
928 ct_event::ModifierKeyCode::LeftShift
929 )),
930 None
931 );
932 }
933
934 #[test]
935 fn map_media_key_known_and_unknown_variants() {
936 assert_eq!(
938 map_media_key(ct_event::MediaKeyCode::Play),
939 Some(KeyCode::MediaPlayPause)
940 );
941 assert_eq!(
942 map_media_key(ct_event::MediaKeyCode::Pause),
943 Some(KeyCode::MediaPlayPause)
944 );
945 assert_eq!(
946 map_media_key(ct_event::MediaKeyCode::PlayPause),
947 Some(KeyCode::MediaPlayPause)
948 );
949 assert_eq!(
950 map_media_key(ct_event::MediaKeyCode::TrackNext),
951 Some(KeyCode::MediaNextTrack)
952 );
953 assert_eq!(
954 map_media_key(ct_event::MediaKeyCode::TrackPrevious),
955 Some(KeyCode::MediaPrevTrack)
956 );
957 assert_eq!(
958 map_media_key(ct_event::MediaKeyCode::Stop),
959 Some(KeyCode::MediaStop)
960 );
961
962 assert_eq!(map_media_key(ct_event::MediaKeyCode::Reverse), None);
964 }
965
966 #[test]
967 fn from_crossterm_returns_none_for_unmapped_keys() {
968 let ct_event = ct_event::Event::Key(ct_event::KeyEvent {
969 code: ct_event::KeyCode::CapsLock,
970 modifiers: ct_event::KeyModifiers::NONE,
971 kind: ct_event::KeyEventKind::Press,
972 state: ct_event::KeyEventState::NONE,
973 });
974
975 assert_eq!(Event::from_crossterm(ct_event), None);
976 }
977}