1use crossterm::event::{
2 Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers, MouseButton, MouseEvent, MouseEventKind,
3};
4pub use dartboard_editor::{
5 AppIntent, AppKey, AppKeyCode, AppModifiers, AppPointerButton, AppPointerEvent, AppPointerKind,
6};
7
8fn app_modifiers_from_crossterm(modifiers: KeyModifiers) -> AppModifiers {
9 AppModifiers {
10 ctrl: modifiers.contains(KeyModifiers::CONTROL),
11 alt: modifiers.contains(KeyModifiers::ALT),
12 shift: modifiers.contains(KeyModifiers::SHIFT),
13 meta: modifiers.contains(KeyModifiers::META),
14 }
15}
16
17pub fn app_key_from_crossterm(key: KeyEvent) -> Option<AppKey> {
18 if key.kind != KeyEventKind::Press {
19 return None;
20 }
21
22 let code = match key.code {
23 KeyCode::Backspace => AppKeyCode::Backspace,
24 KeyCode::Enter => AppKeyCode::Enter,
25 KeyCode::Left => AppKeyCode::Left,
26 KeyCode::Right => AppKeyCode::Right,
27 KeyCode::Up => AppKeyCode::Up,
28 KeyCode::Down => AppKeyCode::Down,
29 KeyCode::Home => AppKeyCode::Home,
30 KeyCode::End => AppKeyCode::End,
31 KeyCode::PageUp => AppKeyCode::PageUp,
32 KeyCode::PageDown => AppKeyCode::PageDown,
33 KeyCode::Tab => AppKeyCode::Tab,
34 KeyCode::BackTab => AppKeyCode::BackTab,
35 KeyCode::Delete => AppKeyCode::Delete,
36 KeyCode::Esc => AppKeyCode::Esc,
37 KeyCode::F(n) => AppKeyCode::F(n),
38 KeyCode::Char(ch) => AppKeyCode::Char(ch),
39 _ => return None,
40 };
41
42 Some(AppKey {
43 code,
44 modifiers: app_modifiers_from_crossterm(key.modifiers),
45 })
46}
47
48pub fn app_pointer_event_from_crossterm(mouse: MouseEvent) -> Option<AppPointerEvent> {
49 let map_button = |button: MouseButton| match button {
50 MouseButton::Left => Some(AppPointerButton::Left),
51 MouseButton::Right => Some(AppPointerButton::Right),
52 MouseButton::Middle => Some(AppPointerButton::Middle),
53 };
54
55 let kind = match mouse.kind {
56 MouseEventKind::Down(button) => AppPointerKind::Down(map_button(button)?),
57 MouseEventKind::Up(button) => AppPointerKind::Up(map_button(button)?),
58 MouseEventKind::Drag(button) => AppPointerKind::Drag(map_button(button)?),
59 MouseEventKind::Moved => AppPointerKind::Moved,
60 MouseEventKind::ScrollUp => AppPointerKind::ScrollUp,
61 MouseEventKind::ScrollDown => AppPointerKind::ScrollDown,
62 MouseEventKind::ScrollLeft => AppPointerKind::ScrollLeft,
63 MouseEventKind::ScrollRight => AppPointerKind::ScrollRight,
64 };
65
66 Some(AppPointerEvent {
67 column: mouse.column,
68 row: mouse.row,
69 kind,
70 modifiers: app_modifiers_from_crossterm(mouse.modifiers),
71 })
72}
73
74pub fn app_intent_from_crossterm(event: Event) -> Option<AppIntent> {
75 match event {
76 Event::Key(key) => app_key_from_crossterm(key).map(AppIntent::KeyPress),
77 Event::Mouse(mouse) => app_pointer_event_from_crossterm(mouse).map(AppIntent::Pointer),
78 Event::Paste(data) => Some(AppIntent::Paste(data)),
79 _ => None,
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::{
86 app_intent_from_crossterm, app_key_from_crossterm, app_pointer_event_from_crossterm,
87 AppIntent, AppKey, AppKeyCode, AppModifiers, AppPointerButton, AppPointerEvent,
88 AppPointerKind,
89 };
90 use crossterm::event::{
91 Event, KeyCode, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers, MouseEvent,
92 MouseEventKind,
93 };
94
95 #[test]
96 fn key_adapter_ignores_non_press_events() {
97 let key = KeyEvent {
98 code: KeyCode::Char('x'),
99 modifiers: KeyModifiers::CONTROL,
100 kind: KeyEventKind::Release,
101 state: KeyEventState::NONE,
102 };
103
104 assert_eq!(app_key_from_crossterm(key), None);
105 }
106
107 #[test]
108 fn pointer_adapter_maps_drag_and_modifiers() {
109 let mouse = MouseEvent {
110 kind: MouseEventKind::Drag(crossterm::event::MouseButton::Left),
111 column: 12,
112 row: 7,
113 modifiers: KeyModifiers::SHIFT | KeyModifiers::ALT,
114 };
115
116 assert_eq!(
117 app_pointer_event_from_crossterm(mouse),
118 Some(AppPointerEvent {
119 column: 12,
120 row: 7,
121 kind: AppPointerKind::Drag(AppPointerButton::Left),
122 modifiers: AppModifiers {
123 alt: true,
124 shift: true,
125 ..Default::default()
126 },
127 })
128 );
129 }
130
131 #[test]
132 fn pointer_adapter_maps_horizontal_scroll() {
133 let mouse = MouseEvent {
134 kind: MouseEventKind::ScrollRight,
135 column: 12,
136 row: 7,
137 modifiers: KeyModifiers::NONE,
138 };
139
140 assert_eq!(
141 app_pointer_event_from_crossterm(mouse),
142 Some(AppPointerEvent {
143 column: 12,
144 row: 7,
145 kind: AppPointerKind::ScrollRight,
146 modifiers: AppModifiers::default(),
147 })
148 );
149 }
150
151 #[test]
152 fn event_adapter_maps_paste_and_keys() {
153 assert_eq!(
154 app_intent_from_crossterm(Event::Paste("hi".to_string())),
155 Some(AppIntent::Paste("hi".to_string()))
156 );
157
158 let enter = app_intent_from_crossterm(Event::Key(KeyEvent::new(
159 KeyCode::Enter,
160 KeyModifiers::NONE,
161 )));
162 assert_eq!(
163 enter,
164 Some(AppIntent::KeyPress(AppKey {
165 code: AppKeyCode::Enter,
166 modifiers: AppModifiers::default(),
167 }))
168 );
169 }
170}