1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3use iced_core::keyboard::Event;
69use iced_core::keyboard::Modifiers;
70use iced_core::keyboard::key;
71use kbd::hotkey::Hotkey;
72use kbd::hotkey::Modifier;
73use kbd::key::Key;
74
75mod private {
76 pub trait Sealed {}
77 impl Sealed for iced_core::keyboard::key::Code {}
78 impl Sealed for iced_core::keyboard::key::Physical {}
79 impl Sealed for iced_core::keyboard::Modifiers {}
80 impl Sealed for iced_core::keyboard::Event {}
81}
82
83pub trait IcedKeyExt: private::Sealed {
90 #[must_use]
106 fn to_key(&self) -> Option<Key>;
107}
108
109impl IcedKeyExt for key::Code {
110 #[allow(clippy::too_many_lines)]
111 fn to_key(&self) -> Option<Key> {
112 match self {
113 key::Code::KeyA => Some(Key::A),
115 key::Code::KeyB => Some(Key::B),
116 key::Code::KeyC => Some(Key::C),
117 key::Code::KeyD => Some(Key::D),
118 key::Code::KeyE => Some(Key::E),
119 key::Code::KeyF => Some(Key::F),
120 key::Code::KeyG => Some(Key::G),
121 key::Code::KeyH => Some(Key::H),
122 key::Code::KeyI => Some(Key::I),
123 key::Code::KeyJ => Some(Key::J),
124 key::Code::KeyK => Some(Key::K),
125 key::Code::KeyL => Some(Key::L),
126 key::Code::KeyM => Some(Key::M),
127 key::Code::KeyN => Some(Key::N),
128 key::Code::KeyO => Some(Key::O),
129 key::Code::KeyP => Some(Key::P),
130 key::Code::KeyQ => Some(Key::Q),
131 key::Code::KeyR => Some(Key::R),
132 key::Code::KeyS => Some(Key::S),
133 key::Code::KeyT => Some(Key::T),
134 key::Code::KeyU => Some(Key::U),
135 key::Code::KeyV => Some(Key::V),
136 key::Code::KeyW => Some(Key::W),
137 key::Code::KeyX => Some(Key::X),
138 key::Code::KeyY => Some(Key::Y),
139 key::Code::KeyZ => Some(Key::Z),
140
141 key::Code::Digit0 => Some(Key::DIGIT0),
143 key::Code::Digit1 => Some(Key::DIGIT1),
144 key::Code::Digit2 => Some(Key::DIGIT2),
145 key::Code::Digit3 => Some(Key::DIGIT3),
146 key::Code::Digit4 => Some(Key::DIGIT4),
147 key::Code::Digit5 => Some(Key::DIGIT5),
148 key::Code::Digit6 => Some(Key::DIGIT6),
149 key::Code::Digit7 => Some(Key::DIGIT7),
150 key::Code::Digit8 => Some(Key::DIGIT8),
151 key::Code::Digit9 => Some(Key::DIGIT9),
152
153 key::Code::F1 => Some(Key::F1),
155 key::Code::F2 => Some(Key::F2),
156 key::Code::F3 => Some(Key::F3),
157 key::Code::F4 => Some(Key::F4),
158 key::Code::F5 => Some(Key::F5),
159 key::Code::F6 => Some(Key::F6),
160 key::Code::F7 => Some(Key::F7),
161 key::Code::F8 => Some(Key::F8),
162 key::Code::F9 => Some(Key::F9),
163 key::Code::F10 => Some(Key::F10),
164 key::Code::F11 => Some(Key::F11),
165 key::Code::F12 => Some(Key::F12),
166 key::Code::F13 => Some(Key::F13),
167 key::Code::F14 => Some(Key::F14),
168 key::Code::F15 => Some(Key::F15),
169 key::Code::F16 => Some(Key::F16),
170 key::Code::F17 => Some(Key::F17),
171 key::Code::F18 => Some(Key::F18),
172 key::Code::F19 => Some(Key::F19),
173 key::Code::F20 => Some(Key::F20),
174 key::Code::F21 => Some(Key::F21),
175 key::Code::F22 => Some(Key::F22),
176 key::Code::F23 => Some(Key::F23),
177 key::Code::F24 => Some(Key::F24),
178 key::Code::F25 => Some(Key::F25),
179 key::Code::F26 => Some(Key::F26),
180 key::Code::F27 => Some(Key::F27),
181 key::Code::F28 => Some(Key::F28),
182 key::Code::F29 => Some(Key::F29),
183 key::Code::F30 => Some(Key::F30),
184 key::Code::F31 => Some(Key::F31),
185 key::Code::F32 => Some(Key::F32),
186 key::Code::F33 => Some(Key::F33),
187 key::Code::F34 => Some(Key::F34),
188 key::Code::F35 => Some(Key::F35),
189
190 key::Code::Enter => Some(Key::ENTER),
192 key::Code::Escape => Some(Key::ESCAPE),
193 key::Code::Space => Some(Key::SPACE),
194 key::Code::Tab => Some(Key::TAB),
195 key::Code::Delete => Some(Key::DELETE),
196 key::Code::Backspace => Some(Key::BACKSPACE),
197 key::Code::Insert => Some(Key::INSERT),
198 key::Code::CapsLock => Some(Key::CAPS_LOCK),
199 key::Code::Home => Some(Key::HOME),
200 key::Code::End => Some(Key::END),
201 key::Code::PageUp => Some(Key::PAGE_UP),
202 key::Code::PageDown => Some(Key::PAGE_DOWN),
203 key::Code::ArrowUp => Some(Key::ARROW_UP),
204 key::Code::ArrowDown => Some(Key::ARROW_DOWN),
205 key::Code::ArrowLeft => Some(Key::ARROW_LEFT),
206 key::Code::ArrowRight => Some(Key::ARROW_RIGHT),
207
208 key::Code::Minus => Some(Key::MINUS),
210 key::Code::Equal => Some(Key::EQUAL),
211 key::Code::BracketLeft => Some(Key::BRACKET_LEFT),
212 key::Code::BracketRight => Some(Key::BRACKET_RIGHT),
213 key::Code::Backslash => Some(Key::BACKSLASH),
214 key::Code::Semicolon => Some(Key::SEMICOLON),
215 key::Code::Quote => Some(Key::QUOTE),
216 key::Code::Backquote => Some(Key::BACKQUOTE),
217 key::Code::Comma => Some(Key::COMMA),
218 key::Code::Period => Some(Key::PERIOD),
219 key::Code::Slash => Some(Key::SLASH),
220
221 key::Code::Numpad0 => Some(Key::NUMPAD0),
223 key::Code::Numpad1 => Some(Key::NUMPAD1),
224 key::Code::Numpad2 => Some(Key::NUMPAD2),
225 key::Code::Numpad3 => Some(Key::NUMPAD3),
226 key::Code::Numpad4 => Some(Key::NUMPAD4),
227 key::Code::Numpad5 => Some(Key::NUMPAD5),
228 key::Code::Numpad6 => Some(Key::NUMPAD6),
229 key::Code::Numpad7 => Some(Key::NUMPAD7),
230 key::Code::Numpad8 => Some(Key::NUMPAD8),
231 key::Code::Numpad9 => Some(Key::NUMPAD9),
232 key::Code::NumpadDecimal => Some(Key::NUMPAD_DECIMAL),
233 key::Code::NumpadAdd => Some(Key::NUMPAD_ADD),
234 key::Code::NumpadSubtract => Some(Key::NUMPAD_SUBTRACT),
235 key::Code::NumpadMultiply => Some(Key::NUMPAD_MULTIPLY),
236 key::Code::NumpadDivide => Some(Key::NUMPAD_DIVIDE),
237 key::Code::NumpadEnter => Some(Key::NUMPAD_ENTER),
238 key::Code::NumpadEqual => Some(Key::NUMPAD_EQUAL),
239 key::Code::NumpadComma => Some(Key::NUMPAD_COMMA),
240 key::Code::NumpadBackspace => Some(Key::NUMPAD_BACKSPACE),
241 key::Code::NumpadClear => Some(Key::NUMPAD_CLEAR),
242 key::Code::NumpadClearEntry => Some(Key::NUMPAD_CLEAR_ENTRY),
243 key::Code::NumpadHash => Some(Key::NUMPAD_HASH),
244 key::Code::NumpadMemoryAdd => Some(Key::NUMPAD_MEMORY_ADD),
245 key::Code::NumpadMemoryClear => Some(Key::NUMPAD_MEMORY_CLEAR),
246 key::Code::NumpadMemoryRecall => Some(Key::NUMPAD_MEMORY_RECALL),
247 key::Code::NumpadMemoryStore => Some(Key::NUMPAD_MEMORY_STORE),
248 key::Code::NumpadMemorySubtract => Some(Key::NUMPAD_MEMORY_SUBTRACT),
249 key::Code::NumpadParenLeft => Some(Key::NUMPAD_PAREN_LEFT),
250 key::Code::NumpadParenRight => Some(Key::NUMPAD_PAREN_RIGHT),
251 key::Code::NumpadStar => Some(Key::NUMPAD_STAR),
252
253 key::Code::ControlLeft => Some(Key::CONTROL_LEFT),
256 key::Code::ControlRight => Some(Key::CONTROL_RIGHT),
257 key::Code::ShiftLeft => Some(Key::SHIFT_LEFT),
258 key::Code::ShiftRight => Some(Key::SHIFT_RIGHT),
259 key::Code::AltLeft => Some(Key::ALT_LEFT),
260 key::Code::AltRight => Some(Key::ALT_RIGHT),
261 key::Code::SuperLeft | key::Code::Meta => Some(Key::META_LEFT),
262 key::Code::SuperRight => Some(Key::META_RIGHT),
263
264 key::Code::AudioVolumeUp => Some(Key::AUDIO_VOLUME_UP),
266 key::Code::AudioVolumeDown => Some(Key::AUDIO_VOLUME_DOWN),
267 key::Code::AudioVolumeMute => Some(Key::AUDIO_VOLUME_MUTE),
268 key::Code::MediaPlayPause => Some(Key::MEDIA_PLAY_PAUSE),
269 key::Code::MediaStop => Some(Key::MEDIA_STOP),
270 key::Code::MediaTrackNext => Some(Key::MEDIA_TRACK_NEXT),
271 key::Code::MediaTrackPrevious => Some(Key::MEDIA_TRACK_PREVIOUS),
272 key::Code::MediaSelect => Some(Key::MEDIA_SELECT),
273
274 key::Code::BrowserBack => Some(Key::BROWSER_BACK),
276 key::Code::BrowserFavorites => Some(Key::BROWSER_FAVORITES),
277 key::Code::BrowserForward => Some(Key::BROWSER_FORWARD),
278 key::Code::BrowserHome => Some(Key::BROWSER_HOME),
279 key::Code::BrowserRefresh => Some(Key::BROWSER_REFRESH),
280 key::Code::BrowserSearch => Some(Key::BROWSER_SEARCH),
281 key::Code::BrowserStop => Some(Key::BROWSER_STOP),
282
283 key::Code::PrintScreen => Some(Key::PRINT_SCREEN),
285 key::Code::ScrollLock => Some(Key::SCROLL_LOCK),
286 key::Code::Pause => Some(Key::PAUSE),
287 key::Code::NumLock => Some(Key::NUM_LOCK),
288 key::Code::ContextMenu => Some(Key::CONTEXT_MENU),
289 key::Code::Power => Some(Key::POWER),
290 key::Code::Sleep => Some(Key::SLEEP),
291 key::Code::WakeUp => Some(Key::WAKE_UP),
292 key::Code::Eject => Some(Key::EJECT),
293
294 key::Code::Copy => Some(Key::COPY),
296 key::Code::Cut => Some(Key::CUT),
297 key::Code::Paste => Some(Key::PASTE),
298 key::Code::Undo => Some(Key::UNDO),
299 key::Code::Find => Some(Key::FIND),
300 key::Code::Help => Some(Key::HELP),
301 key::Code::Open => Some(Key::OPEN),
302 key::Code::Select => Some(Key::SELECT),
303 key::Code::Again => Some(Key::AGAIN),
304 key::Code::Props => Some(Key::PROPS),
305 key::Code::Abort => Some(Key::ABORT),
306 key::Code::Resume => Some(Key::RESUME),
307 key::Code::Suspend => Some(Key::SUSPEND),
308
309 key::Code::Fn => Some(Key::FN),
311 key::Code::FnLock => Some(Key::FN_LOCK),
312 key::Code::Hyper => Some(Key::HYPER),
313 key::Code::Turbo => Some(Key::TURBO),
314
315 key::Code::Convert => Some(Key::CONVERT),
317 key::Code::NonConvert => Some(Key::NON_CONVERT),
318 key::Code::KanaMode => Some(Key::KANA_MODE),
319 key::Code::Hiragana => Some(Key::HIRAGANA),
320 key::Code::Katakana => Some(Key::KATAKANA),
321 key::Code::Lang1 => Some(Key::LANG1),
322 key::Code::Lang2 => Some(Key::LANG2),
323 key::Code::Lang3 => Some(Key::LANG3),
324 key::Code::Lang4 => Some(Key::LANG4),
325 key::Code::Lang5 => Some(Key::LANG5),
326 key::Code::IntlBackslash => Some(Key::INTL_BACKSLASH),
327 key::Code::IntlRo => Some(Key::INTL_RO),
328 key::Code::IntlYen => Some(Key::INTL_YEN),
329
330 key::Code::LaunchApp1 => Some(Key::LAUNCH_APP1),
332 key::Code::LaunchApp2 => Some(Key::LAUNCH_APP2),
333 key::Code::LaunchMail => Some(Key::LAUNCH_MAIL),
334
335 _ => None,
336 }
337 }
338}
339
340impl IcedKeyExt for key::Physical {
341 fn to_key(&self) -> Option<Key> {
342 match self {
343 key::Physical::Code(code) => code.to_key(),
344 key::Physical::Unidentified(_) => None,
345 }
346 }
347}
348
349pub trait IcedModifiersExt: private::Sealed {
356 #[must_use]
369 fn to_modifiers(&self) -> Vec<Modifier>;
370}
371
372impl IcedModifiersExt for Modifiers {
373 fn to_modifiers(&self) -> Vec<Modifier> {
374 Modifier::collect_active([
375 (self.control(), Modifier::Ctrl),
376 (self.shift(), Modifier::Shift),
377 (self.alt(), Modifier::Alt),
378 (self.logo(), Modifier::Super),
379 ])
380 }
381}
382
383pub trait IcedEventExt: private::Sealed {
395 #[must_use]
419 fn to_hotkey(&self) -> Option<Hotkey>;
420}
421
422impl IcedEventExt for Event {
423 fn to_hotkey(&self) -> Option<Hotkey> {
424 let (physical_key, modifiers) = match self {
425 Event::KeyPressed {
426 physical_key,
427 modifiers,
428 ..
429 }
430 | Event::KeyReleased {
431 physical_key,
432 modifiers,
433 ..
434 } => (physical_key, modifiers),
435 Event::ModifiersChanged(_) => return None,
436 };
437
438 let key = physical_key.to_key()?;
439 let mut mods = modifiers.to_modifiers();
440
441 if let Some(self_modifier) = Modifier::from_key(key) {
443 mods.retain(|m| *m != self_modifier);
444 }
445
446 Some(Hotkey::with_modifiers(key, mods))
447 }
448}
449
450#[cfg(test)]
451mod tests {
452 use iced_core::keyboard::Event;
453 use iced_core::keyboard::Location;
454 use iced_core::keyboard::Modifiers;
455 use iced_core::keyboard::key;
456 use kbd::hotkey::Hotkey;
457 use kbd::hotkey::Modifier;
458 use kbd::key::Key;
459
460 use super::*;
461
462 #[test]
465 fn code_letters() {
466 assert_eq!(key::Code::KeyA.to_key(), Some(Key::A));
467 assert_eq!(key::Code::KeyZ.to_key(), Some(Key::Z));
468 }
469
470 #[test]
471 fn code_digits() {
472 assert_eq!(key::Code::Digit0.to_key(), Some(Key::DIGIT0));
473 assert_eq!(key::Code::Digit9.to_key(), Some(Key::DIGIT9));
474 }
475
476 #[test]
477 fn code_function_keys() {
478 assert_eq!(key::Code::F1.to_key(), Some(Key::F1));
479 assert_eq!(key::Code::F12.to_key(), Some(Key::F12));
480 assert_eq!(key::Code::F24.to_key(), Some(Key::F24));
481 assert_eq!(key::Code::F25.to_key(), Some(Key::F25));
482 }
483
484 #[test]
485 fn code_navigation() {
486 assert_eq!(key::Code::Enter.to_key(), Some(Key::ENTER));
487 assert_eq!(key::Code::Escape.to_key(), Some(Key::ESCAPE));
488 assert_eq!(key::Code::Backspace.to_key(), Some(Key::BACKSPACE));
489 assert_eq!(key::Code::Tab.to_key(), Some(Key::TAB));
490 assert_eq!(key::Code::Space.to_key(), Some(Key::SPACE));
491 assert_eq!(key::Code::Delete.to_key(), Some(Key::DELETE));
492 assert_eq!(key::Code::Insert.to_key(), Some(Key::INSERT));
493 assert_eq!(key::Code::Home.to_key(), Some(Key::HOME));
494 assert_eq!(key::Code::End.to_key(), Some(Key::END));
495 assert_eq!(key::Code::PageUp.to_key(), Some(Key::PAGE_UP));
496 assert_eq!(key::Code::PageDown.to_key(), Some(Key::PAGE_DOWN));
497 assert_eq!(key::Code::ArrowUp.to_key(), Some(Key::ARROW_UP));
498 assert_eq!(key::Code::ArrowDown.to_key(), Some(Key::ARROW_DOWN));
499 assert_eq!(key::Code::ArrowLeft.to_key(), Some(Key::ARROW_LEFT));
500 assert_eq!(key::Code::ArrowRight.to_key(), Some(Key::ARROW_RIGHT));
501 }
502
503 #[test]
504 fn code_modifiers() {
505 assert_eq!(key::Code::ControlLeft.to_key(), Some(Key::CONTROL_LEFT));
506 assert_eq!(key::Code::ControlRight.to_key(), Some(Key::CONTROL_RIGHT));
507 assert_eq!(key::Code::ShiftLeft.to_key(), Some(Key::SHIFT_LEFT));
508 assert_eq!(key::Code::ShiftRight.to_key(), Some(Key::SHIFT_RIGHT));
509 assert_eq!(key::Code::AltLeft.to_key(), Some(Key::ALT_LEFT));
510 assert_eq!(key::Code::AltRight.to_key(), Some(Key::ALT_RIGHT));
511 assert_eq!(key::Code::SuperLeft.to_key(), Some(Key::META_LEFT));
513 assert_eq!(key::Code::SuperRight.to_key(), Some(Key::META_RIGHT));
514 assert_eq!(key::Code::Meta.to_key(), Some(Key::META_LEFT));
516 }
517
518 #[test]
519 fn code_punctuation() {
520 assert_eq!(key::Code::Minus.to_key(), Some(Key::MINUS));
521 assert_eq!(key::Code::Equal.to_key(), Some(Key::EQUAL));
522 assert_eq!(key::Code::BracketLeft.to_key(), Some(Key::BRACKET_LEFT));
523 assert_eq!(key::Code::BracketRight.to_key(), Some(Key::BRACKET_RIGHT));
524 assert_eq!(key::Code::Backslash.to_key(), Some(Key::BACKSLASH));
525 assert_eq!(key::Code::Semicolon.to_key(), Some(Key::SEMICOLON));
526 assert_eq!(key::Code::Quote.to_key(), Some(Key::QUOTE));
527 assert_eq!(key::Code::Backquote.to_key(), Some(Key::BACKQUOTE));
528 assert_eq!(key::Code::Comma.to_key(), Some(Key::COMMA));
529 assert_eq!(key::Code::Period.to_key(), Some(Key::PERIOD));
530 assert_eq!(key::Code::Slash.to_key(), Some(Key::SLASH));
531 }
532
533 #[test]
534 fn code_numpad() {
535 assert_eq!(key::Code::Numpad0.to_key(), Some(Key::NUMPAD0));
536 assert_eq!(key::Code::Numpad9.to_key(), Some(Key::NUMPAD9));
537 assert_eq!(key::Code::NumpadDecimal.to_key(), Some(Key::NUMPAD_DECIMAL));
538 assert_eq!(key::Code::NumpadAdd.to_key(), Some(Key::NUMPAD_ADD));
539 assert_eq!(
540 key::Code::NumpadSubtract.to_key(),
541 Some(Key::NUMPAD_SUBTRACT)
542 );
543 assert_eq!(
544 key::Code::NumpadMultiply.to_key(),
545 Some(Key::NUMPAD_MULTIPLY)
546 );
547 assert_eq!(key::Code::NumpadDivide.to_key(), Some(Key::NUMPAD_DIVIDE));
548 assert_eq!(key::Code::NumpadEnter.to_key(), Some(Key::NUMPAD_ENTER));
549 }
550
551 #[test]
552 fn code_media() {
553 assert_eq!(
554 key::Code::MediaPlayPause.to_key(),
555 Some(Key::MEDIA_PLAY_PAUSE)
556 );
557 assert_eq!(key::Code::MediaStop.to_key(), Some(Key::MEDIA_STOP));
558 assert_eq!(
559 key::Code::MediaTrackNext.to_key(),
560 Some(Key::MEDIA_TRACK_NEXT)
561 );
562 assert_eq!(
563 key::Code::MediaTrackPrevious.to_key(),
564 Some(Key::MEDIA_TRACK_PREVIOUS)
565 );
566 assert_eq!(
567 key::Code::AudioVolumeUp.to_key(),
568 Some(Key::AUDIO_VOLUME_UP)
569 );
570 assert_eq!(
571 key::Code::AudioVolumeDown.to_key(),
572 Some(Key::AUDIO_VOLUME_DOWN)
573 );
574 assert_eq!(
575 key::Code::AudioVolumeMute.to_key(),
576 Some(Key::AUDIO_VOLUME_MUTE)
577 );
578 }
579
580 #[test]
581 fn code_system() {
582 assert_eq!(key::Code::PrintScreen.to_key(), Some(Key::PRINT_SCREEN));
583 assert_eq!(key::Code::ScrollLock.to_key(), Some(Key::SCROLL_LOCK));
584 assert_eq!(key::Code::Pause.to_key(), Some(Key::PAUSE));
585 assert_eq!(key::Code::NumLock.to_key(), Some(Key::NUM_LOCK));
586 assert_eq!(key::Code::ContextMenu.to_key(), Some(Key::CONTEXT_MENU));
587 assert_eq!(key::Code::Power.to_key(), Some(Key::POWER));
588 }
589
590 #[test]
591 fn code_extended_keys() {
592 assert_eq!(key::Code::F25.to_key(), Some(Key::F25));
593 assert_eq!(key::Code::F35.to_key(), Some(Key::F35));
594 assert_eq!(key::Code::BrowserBack.to_key(), Some(Key::BROWSER_BACK));
595 assert_eq!(key::Code::Copy.to_key(), Some(Key::COPY));
596 assert_eq!(key::Code::Sleep.to_key(), Some(Key::SLEEP));
597 assert_eq!(key::Code::IntlBackslash.to_key(), Some(Key::INTL_BACKSLASH));
598 assert_eq!(key::Code::NumpadEqual.to_key(), Some(Key::NUMPAD_EQUAL));
599 assert_eq!(key::Code::Fn.to_key(), Some(Key::FN));
600 assert_eq!(key::Code::LaunchMail.to_key(), Some(Key::LAUNCH_MAIL));
601 assert_eq!(key::Code::Convert.to_key(), Some(Key::CONVERT));
602 assert_eq!(key::Code::Lang1.to_key(), Some(Key::LANG1));
603 }
604
605 #[test]
608 fn physical_code_to_key() {
609 let physical = key::Physical::Code(key::Code::KeyA);
610 assert_eq!(physical.to_key(), Some(Key::A));
611 }
612
613 #[test]
614 fn physical_unidentified_returns_none() {
615 let physical = key::Physical::Unidentified(key::NativeCode::Unidentified);
616 assert_eq!(physical.to_key(), None);
617 }
618
619 #[test]
622 fn empty_modifiers() {
623 assert_eq!(Modifiers::empty().to_modifiers(), Vec::<Modifier>::new());
624 }
625
626 #[test]
627 fn single_modifiers() {
628 assert_eq!(Modifiers::CTRL.to_modifiers(), vec![Modifier::Ctrl]);
629 assert_eq!(Modifiers::SHIFT.to_modifiers(), vec![Modifier::Shift]);
630 assert_eq!(Modifiers::ALT.to_modifiers(), vec![Modifier::Alt]);
631 assert_eq!(Modifiers::LOGO.to_modifiers(), vec![Modifier::Super]);
633 }
634
635 #[test]
636 fn combined_modifiers() {
637 let mods = Modifiers::CTRL | Modifiers::SHIFT;
638 assert_eq!(mods.to_modifiers(), vec![Modifier::Ctrl, Modifier::Shift]);
639 }
640
641 #[test]
642 fn all_modifiers() {
643 let mods = Modifiers::CTRL | Modifiers::SHIFT | Modifiers::ALT | Modifiers::LOGO;
644 assert_eq!(
645 mods.to_modifiers(),
646 vec![
647 Modifier::Ctrl,
648 Modifier::Shift,
649 Modifier::Alt,
650 Modifier::Super,
651 ]
652 );
653 }
654
655 fn make_key_pressed(physical_key: key::Physical, modifiers: Modifiers) -> Event {
658 Event::KeyPressed {
659 key: iced_core::keyboard::Key::Unidentified,
660 modified_key: iced_core::keyboard::Key::Unidentified,
661 physical_key,
662 location: Location::Standard,
663 modifiers,
664 text: None,
665 repeat: false,
666 }
667 }
668
669 fn make_key_released(physical_key: key::Physical, modifiers: Modifiers) -> Event {
670 Event::KeyReleased {
671 key: iced_core::keyboard::Key::Unidentified,
672 modified_key: iced_core::keyboard::Key::Unidentified,
673 physical_key,
674 location: Location::Standard,
675 modifiers,
676 }
677 }
678
679 #[test]
680 fn simple_key_press_to_hotkey() {
681 let event = make_key_pressed(key::Physical::Code(key::Code::KeyC), Modifiers::empty());
682 assert_eq!(event.to_hotkey(), Some(Hotkey::new(Key::C)));
683 }
684
685 #[test]
686 fn key_press_with_ctrl_to_hotkey() {
687 let event = make_key_pressed(key::Physical::Code(key::Code::KeyC), Modifiers::CTRL);
688 assert_eq!(
689 event.to_hotkey(),
690 Some(Hotkey::new(Key::C).modifier(Modifier::Ctrl))
691 );
692 }
693
694 #[test]
695 fn key_press_with_multiple_modifiers() {
696 let event = make_key_pressed(
697 key::Physical::Code(key::Code::KeyA),
698 Modifiers::CTRL | Modifiers::SHIFT,
699 );
700 assert_eq!(
701 event.to_hotkey(),
702 Some(
703 Hotkey::new(Key::A)
704 .modifier(Modifier::Ctrl)
705 .modifier(Modifier::Shift)
706 )
707 );
708 }
709
710 #[test]
711 fn key_release_to_hotkey() {
712 let event = make_key_released(key::Physical::Code(key::Code::KeyC), Modifiers::CTRL);
713 assert_eq!(
714 event.to_hotkey(),
715 Some(Hotkey::new(Key::C).modifier(Modifier::Ctrl))
716 );
717 }
718
719 #[test]
720 fn modifiers_changed_returns_none() {
721 let event = Event::ModifiersChanged(Modifiers::CTRL);
722 assert_eq!(event.to_hotkey(), None);
723 }
724
725 #[test]
726 fn unidentified_key_event_returns_none() {
727 let event = make_key_pressed(
728 key::Physical::Unidentified(key::NativeCode::Unidentified),
729 Modifiers::empty(),
730 );
731 assert_eq!(event.to_hotkey(), None);
732 }
733
734 #[test]
735 fn modifier_key_strips_self() {
736 let event = make_key_pressed(key::Physical::Code(key::Code::ShiftLeft), Modifiers::SHIFT);
739 assert_eq!(event.to_hotkey(), Some(Hotkey::new(Key::SHIFT_LEFT)));
740 }
741
742 #[test]
743 fn modifier_key_keeps_other_modifiers() {
744 let event = make_key_pressed(
746 key::Physical::Code(key::Code::ControlLeft),
747 Modifiers::SHIFT | Modifiers::CTRL,
748 );
749 assert_eq!(
750 event.to_hotkey(),
751 Some(Hotkey::new(Key::CONTROL_LEFT).modifier(Modifier::Shift))
752 );
753 }
754
755 #[test]
756 fn ctrl_shift_f5_to_hotkey() {
757 let event = make_key_pressed(
758 key::Physical::Code(key::Code::F5),
759 Modifiers::CTRL | Modifiers::SHIFT,
760 );
761 assert_eq!(
762 event.to_hotkey(),
763 Some(
764 Hotkey::new(Key::F5)
765 .modifier(Modifier::Ctrl)
766 .modifier(Modifier::Shift)
767 )
768 );
769 }
770
771 #[test]
772 fn space_to_hotkey() {
773 let event = make_key_pressed(key::Physical::Code(key::Code::Space), Modifiers::empty());
774 assert_eq!(event.to_hotkey(), Some(Hotkey::new(Key::SPACE)));
775 }
776
777 #[test]
778 fn super_key_strips_self() {
779 let event = make_key_pressed(key::Physical::Code(key::Code::SuperLeft), Modifiers::LOGO);
781 assert_eq!(event.to_hotkey(), Some(Hotkey::new(Key::META_LEFT)));
782 }
783}