1use std::fmt;
4
5#[derive(Clone, Debug, PartialEq, Eq)]
7#[non_exhaustive]
8pub enum Event {
9 Key(KeyEvent),
11 Mouse(MouseEvent),
13 Resize(u16, u16),
15 Paste(String),
17}
18
19#[derive(Clone, Debug, PartialEq, Eq)]
21pub struct KeyEvent {
22 pub code: KeyCode,
24 pub modifiers: Modifiers,
26}
27
28impl KeyEvent {
29 pub fn new(code: KeyCode, modifiers: Modifiers) -> Self {
31 Self { code, modifiers }
32 }
33
34 pub fn plain(code: KeyCode) -> Self {
36 Self {
37 code,
38 modifiers: Modifiers::NONE,
39 }
40 }
41
42 pub fn ctrl(&self) -> bool {
44 self.modifiers.contains(Modifiers::CTRL)
45 }
46
47 pub fn alt(&self) -> bool {
49 self.modifiers.contains(Modifiers::ALT)
50 }
51
52 pub fn shift(&self) -> bool {
54 self.modifiers.contains(Modifiers::SHIFT)
55 }
56}
57
58#[derive(Clone, Debug, PartialEq, Eq, Hash)]
60#[non_exhaustive]
61pub enum KeyCode {
62 Char(char),
64 Enter,
66 Tab,
68 Backspace,
70 Delete,
72 Escape,
74 Up,
76 Down,
78 Left,
80 Right,
82 Home,
84 End,
86 PageUp,
88 PageDown,
90 Insert,
92 F(u8),
94}
95
96#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
98pub struct Modifiers(u8);
99
100impl Modifiers {
101 pub const NONE: Self = Self(0);
103 pub const SHIFT: Self = Self(1);
105 pub const CTRL: Self = Self(2);
107 pub const ALT: Self = Self(4);
109 pub const SUPER: Self = Self(8);
111
112 pub const fn contains(self, other: Self) -> bool {
114 (self.0 & other.0) == other.0 && other.0 != 0
115 }
116
117 pub const fn union(self, other: Self) -> Self {
119 Self(self.0 | other.0)
120 }
121}
122
123impl std::ops::BitOr for Modifiers {
124 type Output = Self;
125 fn bitor(self, rhs: Self) -> Self {
126 Self(self.0 | rhs.0)
127 }
128}
129
130#[derive(Clone, Copy, Debug, PartialEq, Eq)]
132#[non_exhaustive]
133pub enum MouseEventKind {
134 Press,
136 Release,
138 Drag,
140 Move,
142 ScrollUp,
144 ScrollDown,
146}
147
148#[derive(Clone, Debug, PartialEq, Eq)]
150pub struct MouseEvent {
151 pub kind: MouseEventKind,
153 pub x: u16,
155 pub y: u16,
157 pub modifiers: Modifiers,
159}
160
161impl From<crossterm::event::Event> for Event {
164 fn from(ct: crossterm::event::Event) -> Self {
165 match ct {
166 crossterm::event::Event::Key(key) => Event::Key(key.into()),
167 crossterm::event::Event::Mouse(mouse) => Event::Mouse(mouse.into()),
168 crossterm::event::Event::Resize(w, h) => Event::Resize(w, h),
169 crossterm::event::Event::Paste(text) => Event::Paste(text),
170 _ => Event::Key(KeyEvent::plain(KeyCode::Escape)), }
172 }
173}
174
175impl From<crossterm::event::KeyEvent> for KeyEvent {
176 fn from(ct: crossterm::event::KeyEvent) -> Self {
177 Self {
178 code: ct.code.into(),
179 modifiers: ct.modifiers.into(),
180 }
181 }
182}
183
184impl From<crossterm::event::KeyCode> for KeyCode {
185 fn from(ct: crossterm::event::KeyCode) -> Self {
186 match ct {
187 crossterm::event::KeyCode::Char(c) => KeyCode::Char(c),
188 crossterm::event::KeyCode::Enter => KeyCode::Enter,
189 crossterm::event::KeyCode::Tab => KeyCode::Tab,
190 crossterm::event::KeyCode::Backspace => KeyCode::Backspace,
191 crossterm::event::KeyCode::Delete => KeyCode::Delete,
192 crossterm::event::KeyCode::Esc => KeyCode::Escape,
193 crossterm::event::KeyCode::Up => KeyCode::Up,
194 crossterm::event::KeyCode::Down => KeyCode::Down,
195 crossterm::event::KeyCode::Left => KeyCode::Left,
196 crossterm::event::KeyCode::Right => KeyCode::Right,
197 crossterm::event::KeyCode::Home => KeyCode::Home,
198 crossterm::event::KeyCode::End => KeyCode::End,
199 crossterm::event::KeyCode::PageUp => KeyCode::PageUp,
200 crossterm::event::KeyCode::PageDown => KeyCode::PageDown,
201 crossterm::event::KeyCode::Insert => KeyCode::Insert,
202 crossterm::event::KeyCode::F(n) => KeyCode::F(n),
203 _ => KeyCode::Escape, }
205 }
206}
207
208impl From<crossterm::event::KeyModifiers> for Modifiers {
209 fn from(ct: crossterm::event::KeyModifiers) -> Self {
210 let mut m = Modifiers::NONE;
211 if ct.contains(crossterm::event::KeyModifiers::SHIFT) {
212 m = m | Modifiers::SHIFT;
213 }
214 if ct.contains(crossterm::event::KeyModifiers::CONTROL) {
215 m = m | Modifiers::CTRL;
216 }
217 if ct.contains(crossterm::event::KeyModifiers::ALT) {
218 m = m | Modifiers::ALT;
219 }
220 if ct.contains(crossterm::event::KeyModifiers::SUPER) {
221 m = m | Modifiers::SUPER;
222 }
223 m
224 }
225}
226
227impl From<crossterm::event::MouseEvent> for MouseEvent {
228 fn from(ct: crossterm::event::MouseEvent) -> Self {
229 Self {
230 kind: ct.kind.into(),
231 x: ct.column,
232 y: ct.row,
233 modifiers: ct.modifiers.into(),
234 }
235 }
236}
237
238impl From<crossterm::event::MouseEventKind> for MouseEventKind {
239 fn from(ct: crossterm::event::MouseEventKind) -> Self {
240 match ct {
241 crossterm::event::MouseEventKind::Down(_) => MouseEventKind::Press,
242 crossterm::event::MouseEventKind::Up(_) => MouseEventKind::Release,
243 crossterm::event::MouseEventKind::Drag(_) => MouseEventKind::Drag,
244 crossterm::event::MouseEventKind::Moved => MouseEventKind::Move,
245 crossterm::event::MouseEventKind::ScrollUp => MouseEventKind::ScrollUp,
246 crossterm::event::MouseEventKind::ScrollDown => MouseEventKind::ScrollDown,
247 _ => MouseEventKind::Move, }
249 }
250}
251
252impl fmt::Display for KeyCode {
253 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254 match self {
255 KeyCode::Char(c) => write!(f, "{c}"),
256 KeyCode::Enter => write!(f, "Enter"),
257 KeyCode::Tab => write!(f, "Tab"),
258 KeyCode::Backspace => write!(f, "Backspace"),
259 KeyCode::Delete => write!(f, "Delete"),
260 KeyCode::Escape => write!(f, "Escape"),
261 KeyCode::Up => write!(f, "Up"),
262 KeyCode::Down => write!(f, "Down"),
263 KeyCode::Left => write!(f, "Left"),
264 KeyCode::Right => write!(f, "Right"),
265 KeyCode::Home => write!(f, "Home"),
266 KeyCode::End => write!(f, "End"),
267 KeyCode::PageUp => write!(f, "PageUp"),
268 KeyCode::PageDown => write!(f, "PageDown"),
269 KeyCode::Insert => write!(f, "Insert"),
270 KeyCode::F(n) => write!(f, "F{n}"),
271 }
272 }
273}
274
275#[cfg(test)]
276mod tests {
277 use super::*;
278
279 #[test]
280 fn key_event_plain() {
281 let k = KeyEvent::plain(KeyCode::Char('a'));
282 assert!(!k.ctrl());
283 assert!(!k.alt());
284 assert!(!k.shift());
285 }
286
287 #[test]
288 fn key_event_with_modifiers() {
289 let k = KeyEvent::new(KeyCode::Char('c'), Modifiers::CTRL);
290 assert!(k.ctrl());
291 assert!(!k.alt());
292 }
293
294 #[test]
295 fn modifier_union() {
296 let m = Modifiers::CTRL | Modifiers::SHIFT;
297 assert!(m.contains(Modifiers::CTRL));
298 assert!(m.contains(Modifiers::SHIFT));
299 assert!(!m.contains(Modifiers::ALT));
300 }
301
302 #[test]
303 fn resize_event() {
304 let e = Event::Resize(80, 24);
305 assert!(matches!(e, Event::Resize(80, 24)));
306 }
307
308 #[test]
309 fn paste_event() {
310 let e = Event::Paste("hello".into());
311 assert!(matches!(e, Event::Paste(ref s) if s == "hello"));
312 }
313
314 #[test]
315 fn mouse_event() {
316 let m = MouseEvent {
317 kind: MouseEventKind::Press,
318 x: 10,
319 y: 5,
320 modifiers: Modifiers::NONE,
321 };
322 assert_eq!(m.kind, MouseEventKind::Press);
323 assert_eq!(m.x, 10);
324 assert_eq!(m.y, 5);
325 }
326
327 #[test]
328 fn keycode_display() {
329 assert_eq!(format!("{}", KeyCode::Char('a')), "a");
330 assert_eq!(format!("{}", KeyCode::Enter), "Enter");
331 assert_eq!(format!("{}", KeyCode::F(1)), "F1");
332 }
333
334 #[test]
335 fn crossterm_key_conversion() {
336 let ct = crossterm::event::KeyEvent::new(
337 crossterm::event::KeyCode::Char('x'),
338 crossterm::event::KeyModifiers::CONTROL,
339 );
340 let k: KeyEvent = ct.into();
341 assert_eq!(k.code, KeyCode::Char('x'));
342 assert!(k.ctrl());
343 }
344
345 #[test]
346 fn crossterm_resize_conversion() {
347 let ct = crossterm::event::Event::Resize(120, 40);
348 let e: Event = ct.into();
349 assert!(matches!(e, Event::Resize(120, 40)));
350 }
351}