1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3use crossterm::event::KeyCode;
68use crossterm::event::KeyEvent;
69use crossterm::event::KeyModifiers;
70use crossterm::event::MediaKeyCode;
71use crossterm::event::ModifierKeyCode;
72use kbd::hotkey::Hotkey;
73use kbd::hotkey::Modifier;
74use kbd::key::Key;
75
76mod private {
77 pub trait Sealed {}
78 impl Sealed for crossterm::event::KeyCode {}
79 impl Sealed for crossterm::event::KeyModifiers {}
80 impl Sealed for crossterm::event::KeyEvent {}
81}
82
83pub trait CrosstermKeyExt: private::Sealed {
90 #[must_use]
104 fn to_key(&self) -> Option<Key>;
105}
106
107impl CrosstermKeyExt for KeyCode {
108 fn to_key(&self) -> Option<Key> {
109 match self {
110 KeyCode::Char(ch) => char_to_key(*ch),
111 KeyCode::F(n) => function_key(*n),
112 KeyCode::Enter => Some(Key::ENTER),
113 KeyCode::Esc => Some(Key::ESCAPE),
114 KeyCode::Backspace => Some(Key::BACKSPACE),
115 KeyCode::Tab => Some(Key::TAB),
116 KeyCode::Delete => Some(Key::DELETE),
117 KeyCode::Insert => Some(Key::INSERT),
118 KeyCode::Home => Some(Key::HOME),
119 KeyCode::End => Some(Key::END),
120 KeyCode::PageUp => Some(Key::PAGE_UP),
121 KeyCode::PageDown => Some(Key::PAGE_DOWN),
122 KeyCode::Up => Some(Key::ARROW_UP),
123 KeyCode::Down => Some(Key::ARROW_DOWN),
124 KeyCode::Left => Some(Key::ARROW_LEFT),
125 KeyCode::Right => Some(Key::ARROW_RIGHT),
126 KeyCode::CapsLock => Some(Key::CAPS_LOCK),
127 KeyCode::ScrollLock => Some(Key::SCROLL_LOCK),
128 KeyCode::NumLock => Some(Key::NUM_LOCK),
129 KeyCode::PrintScreen => Some(Key::PRINT_SCREEN),
130 KeyCode::Pause => Some(Key::PAUSE),
131 KeyCode::Menu => Some(Key::CONTEXT_MENU),
132 KeyCode::Media(media) => media_to_key(*media),
133 KeyCode::Modifier(modifier) => modifier_keycode_to_key(*modifier),
134 KeyCode::BackTab | KeyCode::Null | KeyCode::KeypadBegin => None,
135 }
136 }
137}
138
139pub trait CrosstermModifiersExt: private::Sealed {
146 #[must_use]
159 fn to_modifiers(&self) -> Vec<Modifier>;
160}
161
162impl CrosstermModifiersExt for KeyModifiers {
163 fn to_modifiers(&self) -> Vec<Modifier> {
164 Modifier::collect_active([
165 (self.contains(KeyModifiers::CONTROL), Modifier::Ctrl),
166 (self.contains(KeyModifiers::SHIFT), Modifier::Shift),
167 (self.contains(KeyModifiers::ALT), Modifier::Alt),
168 (self.contains(KeyModifiers::SUPER), Modifier::Super),
169 ])
170 }
171}
172
173pub trait CrosstermEventExt: private::Sealed {
184 #[must_use]
200 fn to_hotkey(&self) -> Option<Hotkey>;
201}
202
203impl CrosstermEventExt for KeyEvent {
204 fn to_hotkey(&self) -> Option<Hotkey> {
205 let key = self.code.to_key()?;
206 let mut flags = self.modifiers;
207
208 if let Some(self_modifier) = modifier_keycode_flag(self.code) {
212 flags.remove(self_modifier);
213 }
214
215 let modifiers = flags.to_modifiers();
216 Some(Hotkey::with_modifiers(key, modifiers))
217 }
218}
219
220fn char_to_key(ch: char) -> Option<Key> {
221 match ch.to_ascii_uppercase() {
222 'A' => Some(Key::A),
223 'B' => Some(Key::B),
224 'C' => Some(Key::C),
225 'D' => Some(Key::D),
226 'E' => Some(Key::E),
227 'F' => Some(Key::F),
228 'G' => Some(Key::G),
229 'H' => Some(Key::H),
230 'I' => Some(Key::I),
231 'J' => Some(Key::J),
232 'K' => Some(Key::K),
233 'L' => Some(Key::L),
234 'M' => Some(Key::M),
235 'N' => Some(Key::N),
236 'O' => Some(Key::O),
237 'P' => Some(Key::P),
238 'Q' => Some(Key::Q),
239 'R' => Some(Key::R),
240 'S' => Some(Key::S),
241 'T' => Some(Key::T),
242 'U' => Some(Key::U),
243 'V' => Some(Key::V),
244 'W' => Some(Key::W),
245 'X' => Some(Key::X),
246 'Y' => Some(Key::Y),
247 'Z' => Some(Key::Z),
248 '0' => Some(Key::DIGIT0),
249 '1' => Some(Key::DIGIT1),
250 '2' => Some(Key::DIGIT2),
251 '3' => Some(Key::DIGIT3),
252 '4' => Some(Key::DIGIT4),
253 '5' => Some(Key::DIGIT5),
254 '6' => Some(Key::DIGIT6),
255 '7' => Some(Key::DIGIT7),
256 '8' => Some(Key::DIGIT8),
257 '9' => Some(Key::DIGIT9),
258 ' ' => Some(Key::SPACE),
259 '-' => Some(Key::MINUS),
260 '=' => Some(Key::EQUAL),
261 '[' => Some(Key::BRACKET_LEFT),
262 ']' => Some(Key::BRACKET_RIGHT),
263 '\\' => Some(Key::BACKSLASH),
264 ';' => Some(Key::SEMICOLON),
265 '\'' => Some(Key::QUOTE),
266 '`' => Some(Key::BACKQUOTE),
267 ',' => Some(Key::COMMA),
268 '.' => Some(Key::PERIOD),
269 '/' => Some(Key::SLASH),
270 _ => None,
271 }
272}
273
274fn function_key(n: u8) -> Option<Key> {
275 match n {
276 1 => Some(Key::F1),
277 2 => Some(Key::F2),
278 3 => Some(Key::F3),
279 4 => Some(Key::F4),
280 5 => Some(Key::F5),
281 6 => Some(Key::F6),
282 7 => Some(Key::F7),
283 8 => Some(Key::F8),
284 9 => Some(Key::F9),
285 10 => Some(Key::F10),
286 11 => Some(Key::F11),
287 12 => Some(Key::F12),
288 13 => Some(Key::F13),
289 14 => Some(Key::F14),
290 15 => Some(Key::F15),
291 16 => Some(Key::F16),
292 17 => Some(Key::F17),
293 18 => Some(Key::F18),
294 19 => Some(Key::F19),
295 20 => Some(Key::F20),
296 21 => Some(Key::F21),
297 22 => Some(Key::F22),
298 23 => Some(Key::F23),
299 24 => Some(Key::F24),
300 25 => Some(Key::F25),
301 26 => Some(Key::F26),
302 27 => Some(Key::F27),
303 28 => Some(Key::F28),
304 29 => Some(Key::F29),
305 30 => Some(Key::F30),
306 31 => Some(Key::F31),
307 32 => Some(Key::F32),
308 33 => Some(Key::F33),
309 34 => Some(Key::F34),
310 35 => Some(Key::F35),
311 _ => None,
312 }
313}
314
315fn media_to_key(media: MediaKeyCode) -> Option<Key> {
316 match media {
317 MediaKeyCode::PlayPause => Some(Key::MEDIA_PLAY_PAUSE),
318 MediaKeyCode::Stop => Some(Key::MEDIA_STOP),
319 MediaKeyCode::TrackNext => Some(Key::MEDIA_TRACK_NEXT),
320 MediaKeyCode::TrackPrevious => Some(Key::MEDIA_TRACK_PREVIOUS),
321 MediaKeyCode::RaiseVolume => Some(Key::AUDIO_VOLUME_UP),
322 MediaKeyCode::LowerVolume => Some(Key::AUDIO_VOLUME_DOWN),
323 MediaKeyCode::MuteVolume => Some(Key::AUDIO_VOLUME_MUTE),
324 MediaKeyCode::Play => Some(Key::MEDIA_PLAY),
325 MediaKeyCode::Pause => Some(Key::MEDIA_PAUSE),
326 MediaKeyCode::FastForward => Some(Key::MEDIA_FAST_FORWARD),
327 MediaKeyCode::Rewind => Some(Key::MEDIA_REWIND),
328 MediaKeyCode::Record => Some(Key::MEDIA_RECORD),
329 MediaKeyCode::Reverse => None,
330 }
331}
332
333fn modifier_keycode_to_key(modifier: ModifierKeyCode) -> Option<Key> {
334 match modifier {
335 ModifierKeyCode::LeftControl => Some(Key::CONTROL_LEFT),
336 ModifierKeyCode::RightControl => Some(Key::CONTROL_RIGHT),
337 ModifierKeyCode::LeftShift => Some(Key::SHIFT_LEFT),
338 ModifierKeyCode::RightShift => Some(Key::SHIFT_RIGHT),
339 ModifierKeyCode::LeftAlt => Some(Key::ALT_LEFT),
340 ModifierKeyCode::RightAlt => Some(Key::ALT_RIGHT),
341 ModifierKeyCode::LeftSuper => Some(Key::META_LEFT),
342 ModifierKeyCode::RightSuper => Some(Key::META_RIGHT),
343 ModifierKeyCode::LeftHyper | ModifierKeyCode::RightHyper => Some(Key::HYPER),
344 ModifierKeyCode::LeftMeta
345 | ModifierKeyCode::RightMeta
346 | ModifierKeyCode::IsoLevel3Shift
347 | ModifierKeyCode::IsoLevel5Shift => None,
348 }
349}
350
351fn modifier_keycode_flag(code: KeyCode) -> Option<KeyModifiers> {
354 match code {
355 KeyCode::Modifier(ModifierKeyCode::LeftControl | ModifierKeyCode::RightControl) => {
356 Some(KeyModifiers::CONTROL)
357 }
358 KeyCode::Modifier(ModifierKeyCode::LeftShift | ModifierKeyCode::RightShift) => {
359 Some(KeyModifiers::SHIFT)
360 }
361 KeyCode::Modifier(ModifierKeyCode::LeftAlt | ModifierKeyCode::RightAlt) => {
362 Some(KeyModifiers::ALT)
363 }
364 KeyCode::Modifier(ModifierKeyCode::LeftSuper | ModifierKeyCode::RightSuper) => {
365 Some(KeyModifiers::SUPER)
366 }
367 _ => None,
368 }
369}
370
371#[cfg(test)]
372mod tests {
373 use crossterm::event::KeyCode;
374 use crossterm::event::KeyEvent;
375 use crossterm::event::KeyModifiers;
376 use crossterm::event::MediaKeyCode;
377 use crossterm::event::ModifierKeyCode;
378 use kbd::hotkey::Hotkey;
379 use kbd::hotkey::Modifier;
380 use kbd::key::Key;
381
382 use super::*;
383
384 #[test]
387 fn char_lowercase_to_key() {
388 assert_eq!(KeyCode::Char('a').to_key(), Some(Key::A));
389 assert_eq!(KeyCode::Char('z').to_key(), Some(Key::Z));
390 }
391
392 #[test]
393 fn char_uppercase_to_key() {
394 assert_eq!(KeyCode::Char('A').to_key(), Some(Key::A));
395 assert_eq!(KeyCode::Char('Z').to_key(), Some(Key::Z));
396 }
397
398 #[test]
399 fn digit_chars_to_key() {
400 assert_eq!(KeyCode::Char('0').to_key(), Some(Key::DIGIT0));
401 assert_eq!(KeyCode::Char('9').to_key(), Some(Key::DIGIT9));
402 }
403
404 #[test]
405 fn punctuation_chars_to_key() {
406 assert_eq!(KeyCode::Char('-').to_key(), Some(Key::MINUS));
407 assert_eq!(KeyCode::Char('=').to_key(), Some(Key::EQUAL));
408 assert_eq!(KeyCode::Char('[').to_key(), Some(Key::BRACKET_LEFT));
409 assert_eq!(KeyCode::Char(']').to_key(), Some(Key::BRACKET_RIGHT));
410 assert_eq!(KeyCode::Char('\\').to_key(), Some(Key::BACKSLASH));
411 assert_eq!(KeyCode::Char(';').to_key(), Some(Key::SEMICOLON));
412 assert_eq!(KeyCode::Char('\'').to_key(), Some(Key::QUOTE));
413 assert_eq!(KeyCode::Char('`').to_key(), Some(Key::BACKQUOTE));
414 assert_eq!(KeyCode::Char(',').to_key(), Some(Key::COMMA));
415 assert_eq!(KeyCode::Char('.').to_key(), Some(Key::PERIOD));
416 assert_eq!(KeyCode::Char('/').to_key(), Some(Key::SLASH));
417 }
418
419 #[test]
420 fn named_keys_to_key() {
421 assert_eq!(KeyCode::Enter.to_key(), Some(Key::ENTER));
422 assert_eq!(KeyCode::Esc.to_key(), Some(Key::ESCAPE));
423 assert_eq!(KeyCode::Backspace.to_key(), Some(Key::BACKSPACE));
424 assert_eq!(KeyCode::Tab.to_key(), Some(Key::TAB));
425 assert_eq!(KeyCode::Delete.to_key(), Some(Key::DELETE));
426 assert_eq!(KeyCode::Insert.to_key(), Some(Key::INSERT));
427 assert_eq!(KeyCode::Home.to_key(), Some(Key::HOME));
428 assert_eq!(KeyCode::End.to_key(), Some(Key::END));
429 assert_eq!(KeyCode::PageUp.to_key(), Some(Key::PAGE_UP));
430 assert_eq!(KeyCode::PageDown.to_key(), Some(Key::PAGE_DOWN));
431 assert_eq!(KeyCode::Up.to_key(), Some(Key::ARROW_UP));
432 assert_eq!(KeyCode::Down.to_key(), Some(Key::ARROW_DOWN));
433 assert_eq!(KeyCode::Left.to_key(), Some(Key::ARROW_LEFT));
434 assert_eq!(KeyCode::Right.to_key(), Some(Key::ARROW_RIGHT));
435 assert_eq!(KeyCode::CapsLock.to_key(), Some(Key::CAPS_LOCK));
436 assert_eq!(KeyCode::ScrollLock.to_key(), Some(Key::SCROLL_LOCK));
437 assert_eq!(KeyCode::NumLock.to_key(), Some(Key::NUM_LOCK));
438 assert_eq!(KeyCode::PrintScreen.to_key(), Some(Key::PRINT_SCREEN));
439 assert_eq!(KeyCode::Pause.to_key(), Some(Key::PAUSE));
440 assert_eq!(KeyCode::Menu.to_key(), Some(Key::CONTEXT_MENU));
441 }
442
443 #[test]
444 fn function_keys_to_key() {
445 assert_eq!(KeyCode::F(1).to_key(), Some(Key::F1));
446 assert_eq!(KeyCode::F(12).to_key(), Some(Key::F12));
447 assert_eq!(KeyCode::F(24).to_key(), Some(Key::F24));
448 assert_eq!(KeyCode::F(25).to_key(), Some(Key::F25));
449 assert_eq!(KeyCode::F(35).to_key(), Some(Key::F35));
450 assert_eq!(KeyCode::F(36).to_key(), None);
451 assert_eq!(KeyCode::F(0).to_key(), None);
452 }
453
454 #[test]
455 fn media_keys_to_key() {
456 assert_eq!(
457 KeyCode::Media(MediaKeyCode::PlayPause).to_key(),
458 Some(Key::MEDIA_PLAY_PAUSE)
459 );
460 assert_eq!(
461 KeyCode::Media(MediaKeyCode::Stop).to_key(),
462 Some(Key::MEDIA_STOP)
463 );
464 assert_eq!(
465 KeyCode::Media(MediaKeyCode::TrackNext).to_key(),
466 Some(Key::MEDIA_TRACK_NEXT)
467 );
468 assert_eq!(
469 KeyCode::Media(MediaKeyCode::TrackPrevious).to_key(),
470 Some(Key::MEDIA_TRACK_PREVIOUS)
471 );
472 assert_eq!(
473 KeyCode::Media(MediaKeyCode::RaiseVolume).to_key(),
474 Some(Key::AUDIO_VOLUME_UP)
475 );
476 assert_eq!(
477 KeyCode::Media(MediaKeyCode::LowerVolume).to_key(),
478 Some(Key::AUDIO_VOLUME_DOWN)
479 );
480 assert_eq!(
481 KeyCode::Media(MediaKeyCode::MuteVolume).to_key(),
482 Some(Key::AUDIO_VOLUME_MUTE)
483 );
484 }
485
486 #[test]
487 fn extended_media_keys_to_key() {
488 assert_eq!(
489 KeyCode::Media(MediaKeyCode::Play).to_key(),
490 Some(Key::MEDIA_PLAY)
491 );
492 assert_eq!(
493 KeyCode::Media(MediaKeyCode::Pause).to_key(),
494 Some(Key::MEDIA_PAUSE)
495 );
496 assert_eq!(
497 KeyCode::Media(MediaKeyCode::FastForward).to_key(),
498 Some(Key::MEDIA_FAST_FORWARD)
499 );
500 assert_eq!(
501 KeyCode::Media(MediaKeyCode::Rewind).to_key(),
502 Some(Key::MEDIA_REWIND)
503 );
504 assert_eq!(
505 KeyCode::Media(MediaKeyCode::Record).to_key(),
506 Some(Key::MEDIA_RECORD)
507 );
508 }
509
510 #[test]
511 fn modifier_keycode_to_key() {
512 assert_eq!(
513 KeyCode::Modifier(ModifierKeyCode::LeftControl).to_key(),
514 Some(Key::CONTROL_LEFT)
515 );
516 assert_eq!(
517 KeyCode::Modifier(ModifierKeyCode::RightControl).to_key(),
518 Some(Key::CONTROL_RIGHT)
519 );
520 assert_eq!(
521 KeyCode::Modifier(ModifierKeyCode::LeftShift).to_key(),
522 Some(Key::SHIFT_LEFT)
523 );
524 assert_eq!(
525 KeyCode::Modifier(ModifierKeyCode::RightShift).to_key(),
526 Some(Key::SHIFT_RIGHT)
527 );
528 assert_eq!(
529 KeyCode::Modifier(ModifierKeyCode::LeftAlt).to_key(),
530 Some(Key::ALT_LEFT)
531 );
532 assert_eq!(
533 KeyCode::Modifier(ModifierKeyCode::RightAlt).to_key(),
534 Some(Key::ALT_RIGHT)
535 );
536 assert_eq!(
537 KeyCode::Modifier(ModifierKeyCode::LeftSuper).to_key(),
538 Some(Key::META_LEFT)
539 );
540 assert_eq!(
541 KeyCode::Modifier(ModifierKeyCode::RightSuper).to_key(),
542 Some(Key::META_RIGHT)
543 );
544 }
545
546 #[test]
547 fn hyper_modifier_keys_to_key() {
548 assert_eq!(
549 KeyCode::Modifier(ModifierKeyCode::LeftHyper).to_key(),
550 Some(Key::HYPER)
551 );
552 assert_eq!(
553 KeyCode::Modifier(ModifierKeyCode::RightHyper).to_key(),
554 Some(Key::HYPER)
555 );
556 }
557
558 #[test]
559 fn unmappable_keys_return_none() {
560 assert_eq!(KeyCode::Null.to_key(), None);
561 assert_eq!(KeyCode::BackTab.to_key(), None);
562 assert_eq!(KeyCode::KeypadBegin.to_key(), None);
563 }
564
565 #[test]
566 fn non_ascii_chars_return_none() {
567 assert_eq!(KeyCode::Char('é').to_key(), None);
568 assert_eq!(KeyCode::Char('中').to_key(), None);
569 }
570
571 #[test]
572 fn reverse_media_key_returns_none() {
573 assert_eq!(KeyCode::Media(MediaKeyCode::Reverse).to_key(), None);
574 }
575
576 #[test]
577 fn unmappable_modifier_keycodes_return_none() {
578 assert_eq!(KeyCode::Modifier(ModifierKeyCode::LeftMeta).to_key(), None);
579 assert_eq!(KeyCode::Modifier(ModifierKeyCode::RightMeta).to_key(), None);
580 assert_eq!(
581 KeyCode::Modifier(ModifierKeyCode::IsoLevel3Shift).to_key(),
582 None
583 );
584 assert_eq!(
585 KeyCode::Modifier(ModifierKeyCode::IsoLevel5Shift).to_key(),
586 None
587 );
588 }
589
590 #[test]
593 fn empty_modifiers() {
594 assert_eq!(KeyModifiers::NONE.to_modifiers(), Vec::<Modifier>::new());
595 }
596
597 #[test]
598 fn single_modifier() {
599 assert_eq!(KeyModifiers::CONTROL.to_modifiers(), vec![Modifier::Ctrl]);
600 assert_eq!(KeyModifiers::SHIFT.to_modifiers(), vec![Modifier::Shift]);
601 assert_eq!(KeyModifiers::ALT.to_modifiers(), vec![Modifier::Alt]);
602 assert_eq!(KeyModifiers::SUPER.to_modifiers(), vec![Modifier::Super]);
603 }
604
605 #[test]
606 fn combined_modifiers() {
607 let mods = KeyModifiers::CONTROL | KeyModifiers::SHIFT;
608 let result = mods.to_modifiers();
609 assert_eq!(result, vec![Modifier::Ctrl, Modifier::Shift]);
610 }
611
612 #[test]
613 fn all_modifiers() {
614 let mods =
615 KeyModifiers::CONTROL | KeyModifiers::SHIFT | KeyModifiers::ALT | KeyModifiers::SUPER;
616 let result = mods.to_modifiers();
617 assert_eq!(
618 result,
619 vec![
620 Modifier::Ctrl,
621 Modifier::Shift,
622 Modifier::Alt,
623 Modifier::Super
624 ]
625 );
626 }
627
628 #[test]
629 fn hyper_and_meta_ignored() {
630 let mods = KeyModifiers::HYPER | KeyModifiers::META;
631 assert_eq!(mods.to_modifiers(), Vec::<Modifier>::new());
632 }
633
634 #[test]
637 fn simple_key_event_to_hotkey() {
638 let event = KeyEvent::new(KeyCode::Char('c'), KeyModifiers::NONE);
639 let hotkey = event.to_hotkey();
640 assert_eq!(hotkey, Some(Hotkey::new(Key::C)));
641 }
642
643 #[test]
644 fn key_event_with_modifiers_to_hotkey() {
645 let event = KeyEvent::new(KeyCode::Char('c'), KeyModifiers::CONTROL);
646 let hotkey = event.to_hotkey();
647 assert_eq!(hotkey, Some(Hotkey::new(Key::C).modifier(Modifier::Ctrl)));
648 }
649
650 #[test]
651 fn key_event_with_multiple_modifiers() {
652 let event = KeyEvent::new(
653 KeyCode::Char('a'),
654 KeyModifiers::CONTROL | KeyModifiers::SHIFT,
655 );
656 let hotkey = event.to_hotkey();
657 assert_eq!(
658 hotkey,
659 Some(
660 Hotkey::new(Key::A)
661 .modifier(Modifier::Ctrl)
662 .modifier(Modifier::Shift)
663 )
664 );
665 }
666
667 #[test]
668 fn unmappable_key_event_returns_none() {
669 let event = KeyEvent::new(KeyCode::Null, KeyModifiers::NONE);
670 assert_eq!(event.to_hotkey(), None);
671 }
672
673 #[test]
674 fn modifier_key_event_strips_self_modifier() {
675 let event = KeyEvent::new(
679 KeyCode::Modifier(ModifierKeyCode::LeftShift),
680 KeyModifiers::SHIFT,
681 );
682 let hotkey = event.to_hotkey();
683 assert_eq!(hotkey, Some(Hotkey::new(Key::SHIFT_LEFT)));
684 }
685
686 #[test]
687 fn modifier_key_event_keeps_other_modifiers() {
688 let event = KeyEvent::new(
690 KeyCode::Modifier(ModifierKeyCode::LeftControl),
691 KeyModifiers::SHIFT | KeyModifiers::CONTROL,
692 );
693 let hotkey = event.to_hotkey();
694 assert_eq!(
695 hotkey,
696 Some(Hotkey::new(Key::CONTROL_LEFT).modifier(Modifier::Shift))
697 );
698 }
699
700 #[test]
701 fn uppercase_char_treated_as_physical_key() {
702 let event = KeyEvent::new(KeyCode::Char('A'), KeyModifiers::SHIFT);
705 let hotkey = event.to_hotkey();
706 assert_eq!(hotkey, Some(Hotkey::new(Key::A).modifier(Modifier::Shift)));
707 }
708
709 #[test]
710 fn space_key_event() {
711 let event = KeyEvent::new(KeyCode::Char(' '), KeyModifiers::NONE);
712 let hotkey = event.to_hotkey();
713 assert_eq!(hotkey, Some(Hotkey::new(Key::SPACE)));
714 }
715
716 #[test]
717 fn ctrl_shift_f5() {
718 let event = KeyEvent::new(KeyCode::F(5), KeyModifiers::CONTROL | KeyModifiers::SHIFT);
719 let hotkey = event.to_hotkey();
720 assert_eq!(
721 hotkey,
722 Some(
723 Hotkey::new(Key::F5)
724 .modifier(Modifier::Ctrl)
725 .modifier(Modifier::Shift)
726 )
727 );
728 }
729}