1#![allow(clippy::unwrap_used, clippy::disallowed_methods)]
2use crate::event::Key;
11use crate::widget::WidgetId;
12use std::collections::HashMap;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
16pub struct Modifiers {
17 pub ctrl: bool,
19 pub alt: bool,
21 pub shift: bool,
23 pub meta: bool,
25}
26
27impl Modifiers {
28 pub const NONE: Self = Self {
30 ctrl: false,
31 alt: false,
32 shift: false,
33 meta: false,
34 };
35
36 pub const CTRL: Self = Self {
38 ctrl: true,
39 alt: false,
40 shift: false,
41 meta: false,
42 };
43
44 pub const ALT: Self = Self {
46 ctrl: false,
47 alt: true,
48 shift: false,
49 meta: false,
50 };
51
52 pub const SHIFT: Self = Self {
54 ctrl: false,
55 alt: false,
56 shift: true,
57 meta: false,
58 };
59
60 pub const META: Self = Self {
62 ctrl: false,
63 alt: false,
64 shift: false,
65 meta: true,
66 };
67
68 pub const CTRL_SHIFT: Self = Self {
70 ctrl: true,
71 alt: false,
72 shift: true,
73 meta: false,
74 };
75
76 pub const CTRL_ALT: Self = Self {
78 ctrl: true,
79 alt: true,
80 shift: false,
81 meta: false,
82 };
83
84 pub const fn new(ctrl: bool, alt: bool, shift: bool, meta: bool) -> Self {
86 Self {
87 ctrl,
88 alt,
89 shift,
90 meta,
91 }
92 }
93
94 pub const fn any(&self) -> bool {
96 self.ctrl || self.alt || self.shift || self.meta
97 }
98
99 pub const fn none(&self) -> bool {
101 !self.any()
102 }
103
104 pub fn display(&self) -> String {
106 let mut parts = Vec::new();
107 if self.ctrl {
108 parts.push("Ctrl");
109 }
110 if self.alt {
111 parts.push("Alt");
112 }
113 if self.shift {
114 parts.push("Shift");
115 }
116 if self.meta {
117 parts.push("Meta");
118 }
119 parts.join("+")
120 }
121}
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
125pub struct Shortcut {
126 pub key: Key,
128 pub modifiers: Modifiers,
130}
131
132impl Shortcut {
133 pub const fn new(key: Key, modifiers: Modifiers) -> Self {
135 Self { key, modifiers }
136 }
137
138 pub const fn key(key: Key) -> Self {
140 Self::new(key, Modifiers::NONE)
141 }
142
143 pub const fn ctrl(key: Key) -> Self {
145 Self::new(key, Modifiers::CTRL)
146 }
147
148 pub const fn alt(key: Key) -> Self {
150 Self::new(key, Modifiers::ALT)
151 }
152
153 pub const fn shift(key: Key) -> Self {
155 Self::new(key, Modifiers::SHIFT)
156 }
157
158 pub const fn ctrl_shift(key: Key) -> Self {
160 Self::new(key, Modifiers::CTRL_SHIFT)
161 }
162
163 pub fn display(&self) -> String {
165 let key_name = format!("{:?}", self.key);
166 if self.modifiers.none() {
167 key_name
168 } else {
169 format!("{}+{}", self.modifiers.display(), key_name)
170 }
171 }
172
173 pub const COPY: Self = Self::ctrl(Key::C);
175 pub const CUT: Self = Self::ctrl(Key::X);
176 pub const PASTE: Self = Self::ctrl(Key::V);
177 pub const UNDO: Self = Self::ctrl(Key::Z);
178 pub const REDO: Self = Self::ctrl_shift(Key::Z);
179 pub const SAVE: Self = Self::ctrl(Key::S);
180 pub const SELECT_ALL: Self = Self::ctrl(Key::A);
181 pub const FIND: Self = Self::ctrl(Key::F);
182 pub const ESCAPE: Self = Self::key(Key::Escape);
183 pub const ENTER: Self = Self::key(Key::Enter);
184 pub const TAB: Self = Self::key(Key::Tab);
185 pub const DELETE: Self = Self::key(Key::Delete);
186 pub const BACKSPACE: Self = Self::key(Key::Backspace);
187}
188
189#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
191pub struct ShortcutId(pub u64);
192
193impl ShortcutId {
194 pub const fn new(id: u64) -> Self {
196 Self(id)
197 }
198}
199
200#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
202pub enum ShortcutContext {
203 #[default]
205 Global,
206 Widget(WidgetId),
208 WidgetType(String),
210 Custom(String),
212}
213
214#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
216pub enum ShortcutPriority {
217 Low = 0,
219 #[default]
221 Normal = 1,
222 High = 2,
224}
225
226pub type ShortcutHandler = Box<dyn FnMut() -> bool + Send>;
228
229struct ShortcutBinding {
231 #[allow(dead_code)]
232 id: ShortcutId,
233 shortcut: Shortcut,
234 context: ShortcutContext,
235 priority: ShortcutPriority,
236 description: String,
237 enabled: bool,
238}
239
240pub struct ShortcutManager {
242 next_id: u64,
244 bindings: HashMap<ShortcutId, ShortcutBinding>,
246 handlers: HashMap<ShortcutId, ShortcutHandler>,
248 by_shortcut: HashMap<Shortcut, Vec<ShortcutId>>,
250 active_contexts: Vec<ShortcutContext>,
252 modifiers: Modifiers,
254}
255
256impl ShortcutManager {
257 pub fn new() -> Self {
259 Self {
260 next_id: 0,
261 bindings: HashMap::new(),
262 handlers: HashMap::new(),
263 by_shortcut: HashMap::new(),
264 active_contexts: vec![ShortcutContext::Global],
265 modifiers: Modifiers::NONE,
266 }
267 }
268
269 pub fn register(&mut self, shortcut: Shortcut, handler: ShortcutHandler) -> ShortcutId {
271 self.register_with_options(
272 shortcut,
273 handler,
274 ShortcutContext::Global,
275 ShortcutPriority::Normal,
276 "",
277 )
278 }
279
280 pub fn register_with_options(
282 &mut self,
283 shortcut: Shortcut,
284 handler: ShortcutHandler,
285 context: ShortcutContext,
286 priority: ShortcutPriority,
287 description: &str,
288 ) -> ShortcutId {
289 let id = ShortcutId::new(self.next_id);
290 self.next_id += 1;
291
292 let binding = ShortcutBinding {
293 id,
294 shortcut,
295 context,
296 priority,
297 description: description.to_string(),
298 enabled: true,
299 };
300
301 self.bindings.insert(id, binding);
302 self.handlers.insert(id, handler);
303
304 self.by_shortcut.entry(shortcut).or_default().push(id);
305
306 id
307 }
308
309 pub fn unregister(&mut self, id: ShortcutId) -> bool {
311 if let Some(binding) = self.bindings.remove(&id) {
312 self.handlers.remove(&id);
313
314 if let Some(ids) = self.by_shortcut.get_mut(&binding.shortcut) {
315 ids.retain(|&i| i != id);
316 }
317
318 true
319 } else {
320 false
321 }
322 }
323
324 pub fn set_enabled(&mut self, id: ShortcutId, enabled: bool) {
326 if let Some(binding) = self.bindings.get_mut(&id) {
327 binding.enabled = enabled;
328 }
329 }
330
331 pub fn is_enabled(&self, id: ShortcutId) -> bool {
333 self.bindings.get(&id).is_some_and(|b| b.enabled)
334 }
335
336 pub fn set_modifiers(&mut self, modifiers: Modifiers) {
338 self.modifiers = modifiers;
339 }
340
341 pub fn modifiers(&self) -> Modifiers {
343 self.modifiers
344 }
345
346 pub fn push_context(&mut self, context: ShortcutContext) {
348 self.active_contexts.push(context);
349 }
350
351 pub fn pop_context(&mut self) -> Option<ShortcutContext> {
353 if self.active_contexts.len() > 1 {
354 self.active_contexts.pop()
355 } else {
356 None
357 }
358 }
359
360 pub fn set_focused_widget(&mut self, widget_id: Option<WidgetId>) {
362 self.active_contexts
364 .retain(|c| !matches!(c, ShortcutContext::Widget(_)));
365
366 if let Some(id) = widget_id {
367 self.active_contexts.push(ShortcutContext::Widget(id));
368 }
369 }
370
371 pub fn handle_key(&mut self, key: Key) -> bool {
374 let shortcut = Shortcut::new(key, self.modifiers);
375 self.trigger(shortcut)
376 }
377
378 pub fn trigger(&mut self, shortcut: Shortcut) -> bool {
380 let binding_ids = match self.by_shortcut.get(&shortcut) {
381 Some(ids) => ids.clone(),
382 None => return false,
383 };
384
385 let mut matches: Vec<(ShortcutId, ShortcutPriority)> = binding_ids
387 .iter()
388 .filter_map(|&id| {
389 let binding = self.bindings.get(&id)?;
390 if !binding.enabled {
391 return None;
392 }
393 if self.is_context_active(&binding.context) {
394 Some((id, binding.priority))
395 } else {
396 None
397 }
398 })
399 .collect();
400
401 matches.sort_by(|a, b| b.1.cmp(&a.1));
403
404 for (id, _) in matches {
406 if let Some(handler) = self.handlers.get_mut(&id) {
407 if handler() {
408 return true;
409 }
410 }
411 }
412
413 false
414 }
415
416 fn is_context_active(&self, context: &ShortcutContext) -> bool {
418 match context {
419 ShortcutContext::Global => true,
420 other => self.active_contexts.contains(other),
421 }
422 }
423
424 pub fn shortcuts(&self) -> impl Iterator<Item = (&Shortcut, &str)> {
426 self.bindings
427 .values()
428 .map(|b| (&b.shortcut, b.description.as_str()))
429 }
430
431 pub fn binding_count(&self) -> usize {
433 self.bindings.len()
434 }
435
436 pub fn find_conflicts(&self) -> Vec<(Shortcut, Vec<ShortcutId>)> {
438 let mut conflicts = Vec::new();
439
440 for (shortcut, ids) in &self.by_shortcut {
441 if ids.len() < 2 {
442 continue;
443 }
444
445 let mut by_context: HashMap<&ShortcutContext, Vec<ShortcutId>> = HashMap::new();
447 for &id in ids {
448 if let Some(binding) = self.bindings.get(&id) {
449 by_context.entry(&binding.context).or_default().push(id);
450 }
451 }
452
453 for (_, context_ids) in by_context {
455 if context_ids.len() > 1 {
456 conflicts.push((*shortcut, context_ids));
457 }
458 }
459 }
460
461 conflicts
462 }
463
464 pub fn clear(&mut self) {
466 self.bindings.clear();
467 self.handlers.clear();
468 self.by_shortcut.clear();
469 }
470
471 pub fn description(&self, id: ShortcutId) -> Option<&str> {
473 self.bindings.get(&id).map(|b| b.description.as_str())
474 }
475}
476
477impl Default for ShortcutManager {
478 fn default() -> Self {
479 Self::new()
480 }
481}
482
483impl std::fmt::Debug for ShortcutManager {
484 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
485 f.debug_struct("ShortcutManager")
486 .field("binding_count", &self.bindings.len())
487 .field("active_contexts", &self.active_contexts)
488 .field("modifiers", &self.modifiers)
489 .finish()
490 }
491}
492
493#[derive(Debug, Clone)]
495pub struct ShortcutBuilder {
496 key: Key,
497 modifiers: Modifiers,
498 context: ShortcutContext,
499 priority: ShortcutPriority,
500 description: String,
501}
502
503impl ShortcutBuilder {
504 pub fn new(key: Key) -> Self {
506 Self {
507 key,
508 modifiers: Modifiers::NONE,
509 context: ShortcutContext::Global,
510 priority: ShortcutPriority::Normal,
511 description: String::new(),
512 }
513 }
514
515 pub fn ctrl(mut self) -> Self {
517 self.modifiers.ctrl = true;
518 self
519 }
520
521 pub fn alt(mut self) -> Self {
523 self.modifiers.alt = true;
524 self
525 }
526
527 pub fn shift(mut self) -> Self {
529 self.modifiers.shift = true;
530 self
531 }
532
533 pub fn meta(mut self) -> Self {
535 self.modifiers.meta = true;
536 self
537 }
538
539 pub fn context(mut self, context: ShortcutContext) -> Self {
541 self.context = context;
542 self
543 }
544
545 pub fn for_widget(mut self, widget_id: WidgetId) -> Self {
547 self.context = ShortcutContext::Widget(widget_id);
548 self
549 }
550
551 pub fn priority(mut self, priority: ShortcutPriority) -> Self {
553 self.priority = priority;
554 self
555 }
556
557 pub fn description(mut self, desc: &str) -> Self {
559 self.description = desc.to_string();
560 self
561 }
562
563 pub fn build(self) -> Shortcut {
565 Shortcut::new(self.key, self.modifiers)
566 }
567
568 pub fn register(self, manager: &mut ShortcutManager, handler: ShortcutHandler) -> ShortcutId {
570 let shortcut = Shortcut::new(self.key, self.modifiers);
571 manager.register_with_options(
572 shortcut,
573 handler,
574 self.context,
575 self.priority,
576 &self.description,
577 )
578 }
579}
580
581#[cfg(test)]
582mod tests {
583 use super::*;
584 use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
585 use std::sync::Arc;
586
587 #[test]
589 fn test_modifiers_constants() {
590 assert!(!Modifiers::NONE.any());
591 assert!(Modifiers::NONE.none());
592
593 assert!(Modifiers::CTRL.ctrl);
594 assert!(!Modifiers::CTRL.alt);
595
596 assert!(Modifiers::CTRL_SHIFT.ctrl);
597 assert!(Modifiers::CTRL_SHIFT.shift);
598 }
599
600 #[test]
601 fn test_modifiers_new() {
602 let mods = Modifiers::new(true, true, false, false);
603 assert!(mods.ctrl);
604 assert!(mods.alt);
605 assert!(!mods.shift);
606 assert!(!mods.meta);
607 }
608
609 #[test]
610 fn test_modifiers_display() {
611 assert_eq!(Modifiers::NONE.display(), "");
612 assert_eq!(Modifiers::CTRL.display(), "Ctrl");
613 assert_eq!(Modifiers::CTRL_SHIFT.display(), "Ctrl+Shift");
614 }
615
616 #[test]
618 fn test_shortcut_new() {
619 let shortcut = Shortcut::new(Key::A, Modifiers::CTRL);
620 assert_eq!(shortcut.key, Key::A);
621 assert!(shortcut.modifiers.ctrl);
622 }
623
624 #[test]
625 fn test_shortcut_constructors() {
626 let key_only = Shortcut::key(Key::Escape);
627 assert!(key_only.modifiers.none());
628
629 let ctrl = Shortcut::ctrl(Key::S);
630 assert!(ctrl.modifiers.ctrl);
631
632 let alt = Shortcut::alt(Key::F4);
633 assert!(alt.modifiers.alt);
634
635 let shift = Shortcut::shift(Key::Tab);
636 assert!(shift.modifiers.shift);
637
638 let ctrl_shift = Shortcut::ctrl_shift(Key::Z);
639 assert!(ctrl_shift.modifiers.ctrl);
640 assert!(ctrl_shift.modifiers.shift);
641 }
642
643 #[test]
644 fn test_shortcut_display() {
645 assert_eq!(Shortcut::key(Key::A).display(), "A");
646 assert_eq!(Shortcut::ctrl(Key::S).display(), "Ctrl+S");
647 assert_eq!(Shortcut::ctrl_shift(Key::Z).display(), "Ctrl+Shift+Z");
648 }
649
650 #[test]
651 fn test_shortcut_constants() {
652 assert_eq!(Shortcut::COPY, Shortcut::ctrl(Key::C));
653 assert_eq!(Shortcut::UNDO, Shortcut::ctrl(Key::Z));
654 assert_eq!(Shortcut::REDO, Shortcut::ctrl_shift(Key::Z));
655 }
656
657 #[test]
658 fn test_shortcut_equality() {
659 let s1 = Shortcut::ctrl(Key::S);
660 let s2 = Shortcut::ctrl(Key::S);
661 let s3 = Shortcut::ctrl(Key::A);
662
663 assert_eq!(s1, s2);
664 assert_ne!(s1, s3);
665 }
666
667 #[test]
669 fn test_shortcut_id() {
670 let id1 = ShortcutId::new(1);
671 let id2 = ShortcutId::new(1);
672 let id3 = ShortcutId::new(2);
673
674 assert_eq!(id1, id2);
675 assert_ne!(id1, id3);
676 }
677
678 #[test]
680 fn test_shortcut_context_default() {
681 assert_eq!(ShortcutContext::default(), ShortcutContext::Global);
682 }
683
684 #[test]
686 fn test_shortcut_priority_ordering() {
687 assert!(ShortcutPriority::High > ShortcutPriority::Normal);
688 assert!(ShortcutPriority::Normal > ShortcutPriority::Low);
689 }
690
691 #[test]
693 fn test_manager_new() {
694 let manager = ShortcutManager::new();
695 assert_eq!(manager.binding_count(), 0);
696 }
697
698 #[test]
699 fn test_manager_register() {
700 let mut manager = ShortcutManager::new();
701
702 let id = manager.register(Shortcut::ctrl(Key::S), Box::new(|| true));
703 assert_eq!(manager.binding_count(), 1);
704 assert!(manager.is_enabled(id));
705 }
706
707 #[test]
708 fn test_manager_unregister() {
709 let mut manager = ShortcutManager::new();
710
711 let id = manager.register(Shortcut::ctrl(Key::S), Box::new(|| true));
712 assert_eq!(manager.binding_count(), 1);
713
714 let removed = manager.unregister(id);
715 assert!(removed);
716 assert_eq!(manager.binding_count(), 0);
717 }
718
719 #[test]
720 fn test_manager_set_enabled() {
721 let mut manager = ShortcutManager::new();
722
723 let id = manager.register(Shortcut::ctrl(Key::S), Box::new(|| true));
724 assert!(manager.is_enabled(id));
725
726 manager.set_enabled(id, false);
727 assert!(!manager.is_enabled(id));
728
729 manager.set_enabled(id, true);
730 assert!(manager.is_enabled(id));
731 }
732
733 #[test]
734 fn test_manager_handle_key() {
735 let triggered = Arc::new(AtomicBool::new(false));
736 let triggered_clone = triggered.clone();
737
738 let mut manager = ShortcutManager::new();
739 manager.register(
740 Shortcut::ctrl(Key::S),
741 Box::new(move || {
742 triggered_clone.store(true, Ordering::SeqCst);
743 true
744 }),
745 );
746
747 manager.set_modifiers(Modifiers::NONE);
749 let result = manager.handle_key(Key::S);
750 assert!(!result);
751 assert!(!triggered.load(Ordering::SeqCst));
752
753 manager.set_modifiers(Modifiers::CTRL);
755 let result = manager.handle_key(Key::S);
756 assert!(result);
757 assert!(triggered.load(Ordering::SeqCst));
758 }
759
760 #[test]
761 fn test_manager_trigger() {
762 let counter = Arc::new(AtomicUsize::new(0));
763 let counter_clone = counter.clone();
764
765 let mut manager = ShortcutManager::new();
766 manager.register(
767 Shortcut::ctrl(Key::C),
768 Box::new(move || {
769 counter_clone.fetch_add(1, Ordering::SeqCst);
770 true
771 }),
772 );
773
774 manager.trigger(Shortcut::ctrl(Key::C));
775 assert_eq!(counter.load(Ordering::SeqCst), 1);
776
777 manager.trigger(Shortcut::ctrl(Key::C));
778 assert_eq!(counter.load(Ordering::SeqCst), 2);
779 }
780
781 #[test]
782 fn test_manager_disabled_shortcut_not_triggered() {
783 let triggered = Arc::new(AtomicBool::new(false));
784 let triggered_clone = triggered.clone();
785
786 let mut manager = ShortcutManager::new();
787 let id = manager.register(
788 Shortcut::ctrl(Key::S),
789 Box::new(move || {
790 triggered_clone.store(true, Ordering::SeqCst);
791 true
792 }),
793 );
794
795 manager.set_enabled(id, false);
796 manager.set_modifiers(Modifiers::CTRL);
797
798 let result = manager.handle_key(Key::S);
799 assert!(!result);
800 assert!(!triggered.load(Ordering::SeqCst));
801 }
802
803 #[test]
804 fn test_manager_context() {
805 let triggered = Arc::new(AtomicBool::new(false));
806 let triggered_clone = triggered.clone();
807
808 let mut manager = ShortcutManager::new();
809 manager.register_with_options(
810 Shortcut::ctrl(Key::S),
811 Box::new(move || {
812 triggered_clone.store(true, Ordering::SeqCst);
813 true
814 }),
815 ShortcutContext::Widget(WidgetId::new(1)),
816 ShortcutPriority::Normal,
817 "",
818 );
819
820 manager.set_modifiers(Modifiers::CTRL);
822 let result = manager.handle_key(Key::S);
823 assert!(!result);
824
825 manager.set_focused_widget(Some(WidgetId::new(1)));
827 let result = manager.handle_key(Key::S);
828 assert!(result);
829 assert!(triggered.load(Ordering::SeqCst));
830 }
831
832 #[test]
833 fn test_manager_priority() {
834 let order = Arc::new(std::sync::Mutex::new(Vec::new()));
835 let order1 = order.clone();
836 let order2 = order.clone();
837
838 let mut manager = ShortcutManager::new();
839
840 manager.register_with_options(
841 Shortcut::ctrl(Key::S),
842 Box::new(move || {
843 order1.lock().unwrap().push("low");
844 false }),
846 ShortcutContext::Global,
847 ShortcutPriority::Low,
848 "",
849 );
850
851 manager.register_with_options(
852 Shortcut::ctrl(Key::S),
853 Box::new(move || {
854 order2.lock().unwrap().push("high");
855 false
856 }),
857 ShortcutContext::Global,
858 ShortcutPriority::High,
859 "",
860 );
861
862 manager.trigger(Shortcut::ctrl(Key::S));
863
864 let order_vec = order.lock().unwrap();
865 assert_eq!(*order_vec, vec!["high", "low"]);
866 }
867
868 #[test]
869 fn test_manager_handler_consumes() {
870 let counter = Arc::new(AtomicUsize::new(0));
871 let c1 = counter.clone();
872 let c2 = counter.clone();
873
874 let mut manager = ShortcutManager::new();
875
876 manager.register_with_options(
877 Shortcut::ctrl(Key::S),
878 Box::new(move || {
879 c1.fetch_add(1, Ordering::SeqCst);
880 true }),
882 ShortcutContext::Global,
883 ShortcutPriority::High,
884 "",
885 );
886
887 manager.register_with_options(
888 Shortcut::ctrl(Key::S),
889 Box::new(move || {
890 c2.fetch_add(1, Ordering::SeqCst);
891 true
892 }),
893 ShortcutContext::Global,
894 ShortcutPriority::Low,
895 "",
896 );
897
898 manager.trigger(Shortcut::ctrl(Key::S));
899
900 assert_eq!(counter.load(Ordering::SeqCst), 1);
902 }
903
904 #[test]
905 fn test_manager_push_pop_context() {
906 let mut manager = ShortcutManager::new();
907
908 manager.push_context(ShortcutContext::Custom("editor".to_string()));
909 manager.push_context(ShortcutContext::Custom("modal".to_string()));
910
911 let popped = manager.pop_context();
912 assert_eq!(popped, Some(ShortcutContext::Custom("modal".to_string())));
913
914 let popped = manager.pop_context();
915 assert_eq!(popped, Some(ShortcutContext::Custom("editor".to_string())));
916
917 let popped = manager.pop_context();
919 assert!(popped.is_none());
920 }
921
922 #[test]
923 fn test_manager_find_conflicts() {
924 let mut manager = ShortcutManager::new();
925
926 let id1 = manager.register(Shortcut::ctrl(Key::S), Box::new(|| true));
927 let id2 = manager.register(Shortcut::ctrl(Key::S), Box::new(|| true));
928 manager.register(Shortcut::ctrl(Key::A), Box::new(|| true)); let conflicts = manager.find_conflicts();
931 assert_eq!(conflicts.len(), 1);
932 assert!(conflicts[0].1.contains(&id1));
933 assert!(conflicts[0].1.contains(&id2));
934 }
935
936 #[test]
937 fn test_manager_shortcuts() {
938 let mut manager = ShortcutManager::new();
939
940 manager.register_with_options(
941 Shortcut::ctrl(Key::S),
942 Box::new(|| true),
943 ShortcutContext::Global,
944 ShortcutPriority::Normal,
945 "Save",
946 );
947
948 manager.register_with_options(
949 Shortcut::ctrl(Key::O),
950 Box::new(|| true),
951 ShortcutContext::Global,
952 ShortcutPriority::Normal,
953 "Open",
954 );
955
956 let shortcuts: Vec<_> = manager.shortcuts().collect();
957 assert_eq!(shortcuts.len(), 2);
958 }
959
960 #[test]
961 fn test_manager_clear() {
962 let mut manager = ShortcutManager::new();
963 manager.register(Shortcut::ctrl(Key::S), Box::new(|| true));
964 manager.register(Shortcut::ctrl(Key::O), Box::new(|| true));
965
966 manager.clear();
967 assert_eq!(manager.binding_count(), 0);
968 }
969
970 #[test]
971 fn test_manager_description() {
972 let mut manager = ShortcutManager::new();
973
974 let id = manager.register_with_options(
975 Shortcut::ctrl(Key::S),
976 Box::new(|| true),
977 ShortcutContext::Global,
978 ShortcutPriority::Normal,
979 "Save document",
980 );
981
982 assert_eq!(manager.description(id), Some("Save document"));
983 }
984
985 #[test]
987 fn test_builder() {
988 let shortcut = ShortcutBuilder::new(Key::S).ctrl().shift().build();
989
990 assert_eq!(shortcut.key, Key::S);
991 assert!(shortcut.modifiers.ctrl);
992 assert!(shortcut.modifiers.shift);
993 assert!(!shortcut.modifiers.alt);
994 }
995
996 #[test]
997 fn test_builder_register() {
998 let mut manager = ShortcutManager::new();
999
1000 let id = ShortcutBuilder::new(Key::S)
1001 .ctrl()
1002 .description("Save")
1003 .register(&mut manager, Box::new(|| true));
1004
1005 assert!(manager.is_enabled(id));
1006 assert_eq!(manager.description(id), Some("Save"));
1007 }
1008
1009 #[test]
1010 fn test_builder_for_widget() {
1011 let builder = ShortcutBuilder::new(Key::Enter).for_widget(WidgetId::new(42));
1012
1013 assert_eq!(builder.context, ShortcutContext::Widget(WidgetId::new(42)));
1014 }
1015}