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 Ime(ImeEvent),
45
46 Focus(bool),
50
51 Clipboard(ClipboardEvent),
53
54 Tick,
60}
61
62impl Event {
63 #[must_use]
65 #[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
66 pub fn from_crossterm(event: cte::Event) -> Option<Self> {
67 map_crossterm_event_internal(event)
68 }
69
70 #[must_use]
72 pub const fn event_type_label(&self) -> &'static str {
73 match self {
74 Event::Key(_) => "key",
75 Event::Mouse(_) => "mouse",
76 Event::Resize { .. } => "resize",
77 Event::Paste(_) => "paste",
78 Event::Ime(_) => "ime",
79 Event::Focus(_) => "focus",
80 Event::Clipboard(_) => "clipboard",
81 Event::Tick => "tick",
82 }
83 }
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq)]
88pub struct KeyEvent {
89 pub code: KeyCode,
91
92 pub modifiers: Modifiers,
94
95 pub kind: KeyEventKind,
97}
98
99impl KeyEvent {
100 #[must_use]
102 pub const fn new(code: KeyCode) -> Self {
103 Self {
104 code,
105 modifiers: Modifiers::NONE,
106 kind: KeyEventKind::Press,
107 }
108 }
109
110 #[must_use]
112 pub const fn with_modifiers(mut self, modifiers: Modifiers) -> Self {
113 self.modifiers = modifiers;
114 self
115 }
116
117 #[must_use]
119 pub const fn with_kind(mut self, kind: KeyEventKind) -> Self {
120 self.kind = kind;
121 self
122 }
123
124 #[must_use]
126 pub fn is_char(&self, c: char) -> bool {
127 matches!(self.code, KeyCode::Char(ch) if ch == c)
128 }
129
130 #[must_use]
132 pub const fn ctrl(&self) -> bool {
133 self.modifiers.contains(Modifiers::CTRL)
134 }
135
136 #[must_use]
138 pub const fn alt(&self) -> bool {
139 self.modifiers.contains(Modifiers::ALT)
140 }
141
142 #[must_use]
144 pub const fn shift(&self) -> bool {
145 self.modifiers.contains(Modifiers::SHIFT)
146 }
147
148 #[must_use]
150 pub const fn super_key(&self) -> bool {
151 self.modifiers.contains(Modifiers::SUPER)
152 }
153}
154
155#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
157pub enum KeyCode {
158 Char(char),
160
161 Enter,
163
164 Escape,
166
167 Backspace,
169
170 Tab,
172
173 BackTab,
175
176 Delete,
178
179 Insert,
181
182 Home,
184
185 End,
187
188 PageUp,
190
191 PageDown,
193
194 Up,
196
197 Down,
199
200 Left,
202
203 Right,
205
206 F(u8),
208
209 Null,
211
212 MediaPlayPause,
214
215 MediaStop,
217
218 MediaNextTrack,
220
221 MediaPrevTrack,
223}
224
225#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
227pub enum KeyEventKind {
228 #[default]
230 Press,
231
232 Repeat,
234
235 Release,
237}
238
239bitflags! {
240 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
242 pub struct Modifiers: u8 {
243 const NONE = 0b0000;
245 const SHIFT = 0b0001;
247 const ALT = 0b0010;
249 const CTRL = 0b0100;
251 const SUPER = 0b1000;
253 }
254}
255
256impl Default for Modifiers {
257 fn default() -> Self {
258 Self::NONE
259 }
260}
261
262#[derive(Debug, Clone, Copy, PartialEq, Eq)]
264pub struct MouseEvent {
265 pub kind: MouseEventKind,
267
268 pub x: u16,
270
271 pub y: u16,
273
274 pub modifiers: Modifiers,
276}
277
278impl MouseEvent {
279 #[must_use]
281 pub const fn new(kind: MouseEventKind, x: u16, y: u16) -> Self {
282 Self {
283 kind,
284 x,
285 y,
286 modifiers: Modifiers::NONE,
287 }
288 }
289
290 #[must_use]
292 pub const fn with_modifiers(mut self, modifiers: Modifiers) -> Self {
293 self.modifiers = modifiers;
294 self
295 }
296
297 #[must_use]
299 pub const fn position(&self) -> (u16, u16) {
300 (self.x, self.y)
301 }
302}
303
304#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
306pub enum MouseEventKind {
307 Down(MouseButton),
309
310 Up(MouseButton),
312
313 Drag(MouseButton),
315
316 Moved,
318
319 ScrollUp,
321
322 ScrollDown,
324
325 ScrollLeft,
327
328 ScrollRight,
330}
331
332#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
334pub enum MouseButton {
335 Left,
337
338 Right,
340
341 Middle,
343}
344
345#[derive(Debug, Clone, PartialEq, Eq)]
347pub struct PasteEvent {
348 pub text: String,
350
351 pub bracketed: bool,
357}
358
359impl PasteEvent {
360 #[must_use]
362 pub fn new(text: impl Into<String>, bracketed: bool) -> Self {
363 Self {
364 text: text.into(),
365 bracketed,
366 }
367 }
368
369 #[must_use]
371 pub fn bracketed(text: impl Into<String>) -> Self {
372 Self::new(text, true)
373 }
374}
375
376#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
378pub enum ImePhase {
379 Start,
381 Update,
383 Commit,
385 Cancel,
387}
388
389#[derive(Debug, Clone, PartialEq, Eq)]
391pub struct ImeEvent {
392 pub phase: ImePhase,
394 pub text: String,
396}
397
398impl ImeEvent {
399 #[must_use]
401 pub fn new(phase: ImePhase, text: impl Into<String>) -> Self {
402 Self {
403 phase,
404 text: text.into(),
405 }
406 }
407
408 #[must_use]
410 pub fn start() -> Self {
411 Self::new(ImePhase::Start, "")
412 }
413
414 #[must_use]
416 pub fn update(preedit: impl Into<String>) -> Self {
417 Self::new(ImePhase::Update, preedit)
418 }
419
420 #[must_use]
422 pub fn commit(text: impl Into<String>) -> Self {
423 Self::new(ImePhase::Commit, text)
424 }
425
426 #[must_use]
428 pub fn cancel() -> Self {
429 Self::new(ImePhase::Cancel, "")
430 }
431}
432
433#[derive(Debug, Clone, PartialEq, Eq)]
437pub struct ClipboardEvent {
438 pub content: String,
440
441 pub source: ClipboardSource,
443}
444
445impl ClipboardEvent {
446 #[must_use]
448 pub fn new(content: impl Into<String>, source: ClipboardSource) -> Self {
449 Self {
450 content: content.into(),
451 source,
452 }
453 }
454}
455
456#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
458pub enum ClipboardSource {
459 Osc52,
461
462 #[default]
464 Unknown,
465}
466
467#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
468fn map_crossterm_event_internal(event: cte::Event) -> Option<Event> {
469 match event {
470 cte::Event::Key(key) => map_key_event(key).map(Event::Key),
471 cte::Event::Mouse(mouse) => Some(Event::Mouse(map_mouse_event(mouse))),
472 cte::Event::Resize(width, height) => Some(Event::Resize { width, height }),
473 cte::Event::Paste(text) => Some(Event::Paste(PasteEvent::bracketed(text))),
474 cte::Event::FocusGained => Some(Event::Focus(true)),
475 cte::Event::FocusLost => Some(Event::Focus(false)),
476 }
477}
478
479#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
480fn map_key_event(event: cte::KeyEvent) -> Option<KeyEvent> {
481 let code = map_key_code(event.code)?;
482 let modifiers = map_modifiers(event.modifiers);
483 let kind = map_key_kind(event.kind);
484 Some(KeyEvent {
485 code,
486 modifiers,
487 kind,
488 })
489}
490
491#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
492fn map_key_kind(kind: cte::KeyEventKind) -> KeyEventKind {
493 match kind {
494 cte::KeyEventKind::Press => KeyEventKind::Press,
495 cte::KeyEventKind::Repeat => KeyEventKind::Repeat,
496 cte::KeyEventKind::Release => KeyEventKind::Release,
497 }
498}
499
500#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
501pub(crate) fn map_key_code(code: cte::KeyCode) -> Option<KeyCode> {
502 match code {
503 cte::KeyCode::Backspace => Some(KeyCode::Backspace),
504 cte::KeyCode::Enter => Some(KeyCode::Enter),
505 cte::KeyCode::Left => Some(KeyCode::Left),
506 cte::KeyCode::Right => Some(KeyCode::Right),
507 cte::KeyCode::Up => Some(KeyCode::Up),
508 cte::KeyCode::Down => Some(KeyCode::Down),
509 cte::KeyCode::Home => Some(KeyCode::Home),
510 cte::KeyCode::End => Some(KeyCode::End),
511 cte::KeyCode::PageUp => Some(KeyCode::PageUp),
512 cte::KeyCode::PageDown => Some(KeyCode::PageDown),
513 cte::KeyCode::Tab => Some(KeyCode::Tab),
514 cte::KeyCode::BackTab => Some(KeyCode::BackTab),
515 cte::KeyCode::Delete => Some(KeyCode::Delete),
516 cte::KeyCode::Insert => Some(KeyCode::Insert),
517 cte::KeyCode::F(n) => Some(KeyCode::F(n)),
518 cte::KeyCode::Char(c) => Some(KeyCode::Char(c)),
519 cte::KeyCode::Null => Some(KeyCode::Null),
520 cte::KeyCode::Esc => Some(KeyCode::Escape),
521 cte::KeyCode::Media(media) => map_media_key(media),
522 _ => None,
523 }
524}
525
526#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
527fn map_media_key(code: cte::MediaKeyCode) -> Option<KeyCode> {
528 match code {
529 cte::MediaKeyCode::Play | cte::MediaKeyCode::Pause | cte::MediaKeyCode::PlayPause => {
530 Some(KeyCode::MediaPlayPause)
531 }
532 cte::MediaKeyCode::Stop => Some(KeyCode::MediaStop),
533 cte::MediaKeyCode::TrackNext => Some(KeyCode::MediaNextTrack),
534 cte::MediaKeyCode::TrackPrevious => Some(KeyCode::MediaPrevTrack),
535 _ => None,
536 }
537}
538
539#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
540fn map_modifiers(modifiers: cte::KeyModifiers) -> Modifiers {
541 let mut mapped = Modifiers::NONE;
542 if modifiers.contains(cte::KeyModifiers::SHIFT) {
543 mapped |= Modifiers::SHIFT;
544 }
545 if modifiers.contains(cte::KeyModifiers::ALT) {
546 mapped |= Modifiers::ALT;
547 }
548 if modifiers.contains(cte::KeyModifiers::CONTROL) {
549 mapped |= Modifiers::CTRL;
550 }
551 if modifiers.contains(cte::KeyModifiers::SUPER)
552 || modifiers.contains(cte::KeyModifiers::HYPER)
553 || modifiers.contains(cte::KeyModifiers::META)
554 {
555 mapped |= Modifiers::SUPER;
556 }
557 mapped
558}
559
560#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
561fn map_mouse_event(event: cte::MouseEvent) -> MouseEvent {
562 let kind = match event.kind {
563 cte::MouseEventKind::Down(button) => MouseEventKind::Down(map_mouse_button(button)),
564 cte::MouseEventKind::Up(button) => MouseEventKind::Up(map_mouse_button(button)),
565 cte::MouseEventKind::Drag(button) => MouseEventKind::Drag(map_mouse_button(button)),
566 cte::MouseEventKind::Moved => MouseEventKind::Moved,
567 cte::MouseEventKind::ScrollUp => MouseEventKind::ScrollUp,
568 cte::MouseEventKind::ScrollDown => MouseEventKind::ScrollDown,
569 cte::MouseEventKind::ScrollLeft => MouseEventKind::ScrollLeft,
570 cte::MouseEventKind::ScrollRight => MouseEventKind::ScrollRight,
571 };
572
573 let (x, y) = sanitize_crossterm_mouse_coords(event.column, event.row);
574 MouseEvent::new(kind, x, y).with_modifiers(map_modifiers(event.modifiers))
575}
576
577#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
578fn map_mouse_button(button: cte::MouseButton) -> MouseButton {
579 match button {
580 cte::MouseButton::Left => MouseButton::Left,
581 cte::MouseButton::Right => MouseButton::Right,
582 cte::MouseButton::Middle => MouseButton::Middle,
583 }
584}
585
586#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
587fn sanitize_crossterm_mouse_coords(x: u16, y: u16) -> (u16, u16) {
588 (x, y)
593}
594
595#[cfg(all(test, not(target_arch = "wasm32"), feature = "crossterm"))]
596mod tests {
597 use super::*;
598 use crossterm::event as ct_event;
599
600 #[test]
601 fn key_event_is_char() {
602 let event = KeyEvent::new(KeyCode::Char('q'));
603 assert!(event.is_char('q'));
604 assert!(!event.is_char('x'));
605 }
606
607 #[test]
608 fn key_event_modifiers() {
609 let event = KeyEvent::new(KeyCode::Char('c')).with_modifiers(Modifiers::CTRL);
610 assert!(event.ctrl());
611 assert!(!event.alt());
612 assert!(!event.shift());
613 assert!(!event.super_key());
614 }
615
616 #[test]
617 fn key_event_combined_modifiers() {
618 let event =
619 KeyEvent::new(KeyCode::Char('s')).with_modifiers(Modifiers::CTRL | Modifiers::SHIFT);
620 assert!(event.ctrl());
621 assert!(event.shift());
622 assert!(!event.alt());
623 }
624
625 #[test]
626 fn key_event_kind() {
627 let press = KeyEvent::new(KeyCode::Enter);
628 assert_eq!(press.kind, KeyEventKind::Press);
629
630 let release = press.with_kind(KeyEventKind::Release);
631 assert_eq!(release.kind, KeyEventKind::Release);
632 }
633
634 #[test]
635 fn mouse_event_position() {
636 let event = MouseEvent::new(MouseEventKind::Down(MouseButton::Left), 10, 20);
637 assert_eq!(event.position(), (10, 20));
638 assert_eq!(event.x, 10);
639 assert_eq!(event.y, 20);
640 }
641
642 #[test]
643 fn mouse_event_with_modifiers() {
644 let event = MouseEvent::new(MouseEventKind::Moved, 0, 0).with_modifiers(Modifiers::ALT);
645 assert_eq!(event.modifiers, Modifiers::ALT);
646 }
647
648 #[test]
649 fn paste_event_creation() {
650 let paste = PasteEvent::bracketed("hello world");
651 assert_eq!(paste.text, "hello world");
652 assert!(paste.bracketed);
653 }
654
655 #[test]
656 fn clipboard_event_creation() {
657 let clip = ClipboardEvent::new("copied text", ClipboardSource::Osc52);
658 assert_eq!(clip.content, "copied text");
659 assert_eq!(clip.source, ClipboardSource::Osc52);
660 }
661
662 #[test]
663 fn event_variants() {
664 let _key = Event::Key(KeyEvent::new(KeyCode::Char('a')));
666 let _mouse = Event::Mouse(MouseEvent::new(
667 MouseEventKind::Down(MouseButton::Left),
668 0,
669 0,
670 ));
671 let _resize = Event::Resize {
672 width: 80,
673 height: 24,
674 };
675 let _paste = Event::Paste(PasteEvent::bracketed("test"));
676 let _ime = Event::Ime(ImeEvent::update("漢"));
677 let _focus = Event::Focus(true);
678 let _clipboard = Event::Clipboard(ClipboardEvent::new("test", ClipboardSource::Unknown));
679 let _tick = Event::Tick;
680 }
681
682 #[test]
683 fn ime_event_constructors() {
684 assert_eq!(ImeEvent::start().phase, ImePhase::Start);
685 assert_eq!(ImeEvent::update("x"), ImeEvent::new(ImePhase::Update, "x"));
686 assert_eq!(ImeEvent::commit("漢").phase, ImePhase::Commit);
687 assert_eq!(ImeEvent::cancel().phase, ImePhase::Cancel);
688 }
689
690 #[test]
691 fn modifiers_default() {
692 assert_eq!(Modifiers::default(), Modifiers::NONE);
693 }
694
695 #[test]
696 fn key_event_kind_default() {
697 assert_eq!(KeyEventKind::default(), KeyEventKind::Press);
698 }
699
700 #[test]
701 fn clipboard_source_default() {
702 assert_eq!(ClipboardSource::default(), ClipboardSource::Unknown);
703 }
704
705 #[test]
706 fn function_keys() {
707 let f1 = KeyEvent::new(KeyCode::F(1));
708 let f12 = KeyEvent::new(KeyCode::F(12));
709 assert_eq!(f1.code, KeyCode::F(1));
710 assert_eq!(f12.code, KeyCode::F(12));
711 }
712
713 #[test]
714 fn event_is_clone_and_eq() {
715 let event = Event::Key(KeyEvent::new(KeyCode::Char('x')));
716 let cloned = event.clone();
717 assert_eq!(event, cloned);
718 }
719
720 #[test]
723 fn map_modifiers_ctrl() {
724 let mapped = map_modifiers(ct_event::KeyModifiers::CONTROL);
725 assert!(mapped.contains(Modifiers::CTRL));
726 assert!(!mapped.contains(Modifiers::SHIFT));
727 }
728
729 #[test]
730 fn map_modifiers_alt() {
731 let mapped = map_modifiers(ct_event::KeyModifiers::ALT);
732 assert!(mapped.contains(Modifiers::ALT));
733 }
734
735 #[test]
736 fn map_modifiers_super_variants() {
737 let super_mapped = map_modifiers(ct_event::KeyModifiers::SUPER);
738 assert!(super_mapped.contains(Modifiers::SUPER));
739
740 let hyper_mapped = map_modifiers(ct_event::KeyModifiers::HYPER);
741 assert!(hyper_mapped.contains(Modifiers::SUPER));
742
743 let meta_mapped = map_modifiers(ct_event::KeyModifiers::META);
744 assert!(meta_mapped.contains(Modifiers::SUPER));
745 }
746
747 #[test]
748 fn map_modifiers_combined() {
749 let combined = ct_event::KeyModifiers::SHIFT | ct_event::KeyModifiers::CONTROL;
750 let mapped = map_modifiers(combined);
751 assert!(mapped.contains(Modifiers::SHIFT));
752 assert!(mapped.contains(Modifiers::CTRL));
753 assert!(!mapped.contains(Modifiers::ALT));
754 }
755
756 #[test]
757 fn map_mouse_button_all() {
758 assert_eq!(
759 map_mouse_button(ct_event::MouseButton::Left),
760 MouseButton::Left
761 );
762 assert_eq!(
763 map_mouse_button(ct_event::MouseButton::Right),
764 MouseButton::Right
765 );
766 assert_eq!(
767 map_mouse_button(ct_event::MouseButton::Middle),
768 MouseButton::Middle
769 );
770 }
771
772 #[test]
773 fn map_mouse_event_down() {
774 let ct_event = ct_event::MouseEvent {
775 kind: ct_event::MouseEventKind::Down(ct_event::MouseButton::Left),
776 column: 10,
777 row: 5,
778 modifiers: ct_event::KeyModifiers::NONE,
779 };
780 let mapped = map_mouse_event(ct_event);
781 assert!(matches!(
782 mapped.kind,
783 MouseEventKind::Down(MouseButton::Left)
784 ));
785 assert_eq!(mapped.x, 10);
786 assert_eq!(mapped.y, 5);
787 }
788
789 #[test]
790 fn map_mouse_event_up() {
791 let ct_event = ct_event::MouseEvent {
792 kind: ct_event::MouseEventKind::Up(ct_event::MouseButton::Right),
793 column: 20,
794 row: 15,
795 modifiers: ct_event::KeyModifiers::NONE,
796 };
797 let mapped = map_mouse_event(ct_event);
798 assert!(matches!(
799 mapped.kind,
800 MouseEventKind::Up(MouseButton::Right)
801 ));
802 assert_eq!(mapped.x, 20);
803 assert_eq!(mapped.y, 15);
804 }
805
806 #[test]
807 fn map_mouse_event_drag() {
808 let ct_event = ct_event::MouseEvent {
809 kind: ct_event::MouseEventKind::Drag(ct_event::MouseButton::Middle),
810 column: 5,
811 row: 10,
812 modifiers: ct_event::KeyModifiers::NONE,
813 };
814 let mapped = map_mouse_event(ct_event);
815 assert!(matches!(
816 mapped.kind,
817 MouseEventKind::Drag(MouseButton::Middle)
818 ));
819 }
820
821 #[test]
822 fn map_mouse_event_moved() {
823 let ct_event = ct_event::MouseEvent {
824 kind: ct_event::MouseEventKind::Moved,
825 column: 0,
826 row: 0,
827 modifiers: ct_event::KeyModifiers::NONE,
828 };
829 let mapped = map_mouse_event(ct_event);
830 assert!(matches!(mapped.kind, MouseEventKind::Moved));
831 }
832
833 #[test]
834 fn map_mouse_event_scroll() {
835 let scroll_up = ct_event::MouseEvent {
836 kind: ct_event::MouseEventKind::ScrollUp,
837 column: 0,
838 row: 0,
839 modifiers: ct_event::KeyModifiers::NONE,
840 };
841 let scroll_down = ct_event::MouseEvent {
842 kind: ct_event::MouseEventKind::ScrollDown,
843 column: 0,
844 row: 0,
845 modifiers: ct_event::KeyModifiers::NONE,
846 };
847 let scroll_left = ct_event::MouseEvent {
848 kind: ct_event::MouseEventKind::ScrollLeft,
849 column: 0,
850 row: 0,
851 modifiers: ct_event::KeyModifiers::NONE,
852 };
853 let scroll_right = ct_event::MouseEvent {
854 kind: ct_event::MouseEventKind::ScrollRight,
855 column: 0,
856 row: 0,
857 modifiers: ct_event::KeyModifiers::NONE,
858 };
859
860 assert!(matches!(
861 map_mouse_event(scroll_up).kind,
862 MouseEventKind::ScrollUp
863 ));
864 assert!(matches!(
865 map_mouse_event(scroll_down).kind,
866 MouseEventKind::ScrollDown
867 ));
868 assert!(matches!(
869 map_mouse_event(scroll_left).kind,
870 MouseEventKind::ScrollLeft
871 ));
872 assert!(matches!(
873 map_mouse_event(scroll_right).kind,
874 MouseEventKind::ScrollRight
875 ));
876 }
877
878 #[test]
879 fn map_mouse_event_modifiers() {
880 let ct_event = ct_event::MouseEvent {
881 kind: ct_event::MouseEventKind::Down(ct_event::MouseButton::Left),
882 column: 0,
883 row: 0,
884 modifiers: ct_event::KeyModifiers::SHIFT | ct_event::KeyModifiers::ALT,
885 };
886 let mapped = map_mouse_event(ct_event);
887 assert!(mapped.modifiers.contains(Modifiers::SHIFT));
888 assert!(mapped.modifiers.contains(Modifiers::ALT));
889 }
890
891 #[test]
892 fn map_mouse_event_respects_large_coordinates() {
893 let ct_event = ct_event::MouseEvent {
894 kind: ct_event::MouseEventKind::Moved,
895 column: 1600,
896 row: 640,
897 modifiers: ct_event::KeyModifiers::NONE,
898 };
899 let mapped = map_mouse_event(ct_event);
900 assert_eq!(mapped.x, 1600);
901 assert_eq!(mapped.y, 640);
902 }
903
904 #[test]
905 fn map_mouse_event_keeps_cell_coordinates() {
906 let ct_event = ct_event::MouseEvent {
907 kind: ct_event::MouseEventKind::Moved,
908 column: 120,
909 row: 40,
910 modifiers: ct_event::KeyModifiers::NONE,
911 };
912 let mapped = map_mouse_event(ct_event);
913 assert_eq!(mapped.x, 120);
914 assert_eq!(mapped.y, 40);
915 }
916
917 #[test]
918 fn map_key_event_char() {
919 let ct_event = ct_event::KeyEvent {
920 code: ct_event::KeyCode::Char('x'),
921 modifiers: ct_event::KeyModifiers::CONTROL,
922 kind: ct_event::KeyEventKind::Press,
923 state: ct_event::KeyEventState::NONE,
924 };
925 let mapped = map_key_event(ct_event).expect("should map");
926 assert_eq!(mapped.code, KeyCode::Char('x'));
927 assert!(mapped.modifiers.contains(Modifiers::CTRL));
928 assert_eq!(mapped.kind, KeyEventKind::Press);
929 }
930
931 #[test]
932 fn map_key_event_function_key() {
933 let ct_event = ct_event::KeyEvent {
934 code: ct_event::KeyCode::F(5),
935 modifiers: ct_event::KeyModifiers::NONE,
936 kind: ct_event::KeyEventKind::Press,
937 state: ct_event::KeyEventState::NONE,
938 };
939 let mapped = map_key_event(ct_event).expect("should map");
940 assert_eq!(mapped.code, KeyCode::F(5));
941 }
942
943 #[test]
944 fn map_crossterm_event_key() {
945 let ct_event = ct_event::Event::Key(ct_event::KeyEvent {
946 code: ct_event::KeyCode::Enter,
947 modifiers: ct_event::KeyModifiers::NONE,
948 kind: ct_event::KeyEventKind::Press,
949 state: ct_event::KeyEventState::NONE,
950 });
951 let mapped = map_crossterm_event_internal(ct_event).expect("should map");
952 assert!(matches!(mapped, Event::Key(_)));
953 }
954
955 #[test]
956 fn map_crossterm_event_mouse() {
957 let ct_event = ct_event::Event::Mouse(ct_event::MouseEvent {
958 kind: ct_event::MouseEventKind::Down(ct_event::MouseButton::Left),
959 column: 10,
960 row: 5,
961 modifiers: ct_event::KeyModifiers::NONE,
962 });
963 let mapped = map_crossterm_event_internal(ct_event).expect("should map");
964 assert!(matches!(mapped, Event::Mouse(_)));
965 }
966
967 #[test]
968 fn map_crossterm_event_resize() {
969 let ct_event = ct_event::Event::Resize(80, 24);
970 let mapped = map_crossterm_event_internal(ct_event).expect("should map");
971 assert!(matches!(
972 mapped,
973 Event::Resize {
974 width: 80,
975 height: 24
976 }
977 ));
978 }
979
980 #[test]
981 fn map_crossterm_event_paste() {
982 let ct_event = ct_event::Event::Paste("hello world".to_string());
983 let mapped = map_crossterm_event_internal(ct_event).expect("should map");
984 match mapped {
985 Event::Paste(paste) => assert_eq!(paste.text, "hello world"),
986 _ => panic!("expected Paste event"),
987 }
988 }
989
990 #[test]
991 fn map_crossterm_event_focus() {
992 let gained = ct_event::Event::FocusGained;
993 let lost = ct_event::Event::FocusLost;
994
995 assert!(matches!(
996 map_crossterm_event_internal(gained),
997 Some(Event::Focus(true))
998 ));
999 assert!(matches!(
1000 map_crossterm_event_internal(lost),
1001 Some(Event::Focus(false))
1002 ));
1003 }
1004
1005 #[test]
1006 fn map_key_kind_repeat_and_release() {
1007 assert_eq!(
1008 map_key_kind(ct_event::KeyEventKind::Repeat),
1009 KeyEventKind::Repeat
1010 );
1011 assert_eq!(
1012 map_key_kind(ct_event::KeyEventKind::Release),
1013 KeyEventKind::Release
1014 );
1015 }
1016
1017 #[test]
1018 fn map_key_code_escape() {
1019 assert_eq!(map_key_code(ct_event::KeyCode::Esc), Some(KeyCode::Escape));
1020 }
1021
1022 #[test]
1023 fn map_key_code_unmapped_returns_none() {
1024 assert_eq!(map_key_code(ct_event::KeyCode::CapsLock), None);
1026 assert_eq!(map_key_code(ct_event::KeyCode::ScrollLock), None);
1027 assert_eq!(map_key_code(ct_event::KeyCode::NumLock), None);
1028 assert_eq!(map_key_code(ct_event::KeyCode::PrintScreen), None);
1029 assert_eq!(map_key_code(ct_event::KeyCode::Pause), None);
1030 assert_eq!(map_key_code(ct_event::KeyCode::Menu), None);
1031 assert_eq!(map_key_code(ct_event::KeyCode::KeypadBegin), None);
1032 assert_eq!(
1033 map_key_code(ct_event::KeyCode::Modifier(
1034 ct_event::ModifierKeyCode::LeftShift
1035 )),
1036 None
1037 );
1038 }
1039
1040 #[test]
1041 fn map_media_key_known_and_unknown_variants() {
1042 assert_eq!(
1044 map_media_key(ct_event::MediaKeyCode::Play),
1045 Some(KeyCode::MediaPlayPause)
1046 );
1047 assert_eq!(
1048 map_media_key(ct_event::MediaKeyCode::Pause),
1049 Some(KeyCode::MediaPlayPause)
1050 );
1051 assert_eq!(
1052 map_media_key(ct_event::MediaKeyCode::PlayPause),
1053 Some(KeyCode::MediaPlayPause)
1054 );
1055 assert_eq!(
1056 map_media_key(ct_event::MediaKeyCode::TrackNext),
1057 Some(KeyCode::MediaNextTrack)
1058 );
1059 assert_eq!(
1060 map_media_key(ct_event::MediaKeyCode::TrackPrevious),
1061 Some(KeyCode::MediaPrevTrack)
1062 );
1063 assert_eq!(
1064 map_media_key(ct_event::MediaKeyCode::Stop),
1065 Some(KeyCode::MediaStop)
1066 );
1067
1068 assert_eq!(map_media_key(ct_event::MediaKeyCode::Reverse), None);
1070 }
1071
1072 #[test]
1073 fn from_crossterm_returns_none_for_unmapped_keys() {
1074 let ct_event = ct_event::Event::Key(ct_event::KeyEvent {
1075 code: ct_event::KeyCode::CapsLock,
1076 modifiers: ct_event::KeyModifiers::NONE,
1077 kind: ct_event::KeyEventKind::Press,
1078 state: ct_event::KeyEventState::NONE,
1079 });
1080
1081 assert_eq!(Event::from_crossterm(ct_event), None);
1082 }
1083}