1use std::rc::Rc;
2
3use collections::HashMap;
4
5use crate::{Action, InvalidKeystrokeError, KeyBindingContextPredicate, Keystroke};
6use smallvec::SmallVec;
7
8/// A keybinding and its associated metadata, from the keymap.
9pub struct KeyBinding {
10 pub(crate) action: Box<dyn Action>,
11 pub(crate) keystrokes: SmallVec<[Keystroke; 2]>,
12 pub(crate) context_predicate: Option<Rc<KeyBindingContextPredicate>>,
13 pub(crate) meta: Option<KeyBindingMetaIndex>,
14}
15
16impl Clone for KeyBinding {
17 fn clone(&self) -> Self {
18 KeyBinding {
19 action: self.action.boxed_clone(),
20 keystrokes: self.keystrokes.clone(),
21 context_predicate: self.context_predicate.clone(),
22 meta: self.meta,
23 }
24 }
25}
26
27impl KeyBinding {
28 /// Construct a new keybinding from the given data. Panics on parse error.
29 pub fn new<A: Action>(keystrokes: &str, action: A, context: Option<&str>) -> Self {
30 let context_predicate = if let Some(context) = context {
31 Some(KeyBindingContextPredicate::parse(context).unwrap().into())
32 } else {
33 None
34 };
35 Self::load(keystrokes, Box::new(action), context_predicate, None).unwrap()
36 }
37
38 /// Load a keybinding from the given raw data.
39 pub fn load(
40 keystrokes: &str,
41 action: Box<dyn Action>,
42 context_predicate: Option<Rc<KeyBindingContextPredicate>>,
43 key_equivalents: Option<&HashMap<char, char>>,
44 ) -> std::result::Result<Self, InvalidKeystrokeError> {
45 let mut keystrokes: SmallVec<[Keystroke; 2]> = keystrokes
46 .split_whitespace()
47 .map(Keystroke::parse)
48 .collect::<std::result::Result<_, _>>()?;
49
50 if let Some(equivalents) = key_equivalents {
51 for keystroke in keystrokes.iter_mut() {
52 if keystroke.key.chars().count() == 1 {
53 if let Some(key) = equivalents.get(&keystroke.key.chars().next().unwrap()) {
54 keystroke.key = key.to_string();
55 }
56 }
57 }
58 }
59
60 Ok(Self {
61 keystrokes,
62 action,
63 context_predicate,
64 meta: None,
65 })
66 }
67
68 /// Set the metadata for this binding.
69 pub fn with_meta(mut self, meta: KeyBindingMetaIndex) -> Self {
70 self.meta = Some(meta);
71 self
72 }
73
74 /// Set the metadata for this binding.
75 pub fn set_meta(&mut self, meta: KeyBindingMetaIndex) {
76 self.meta = Some(meta);
77 }
78
79 /// Check if the given keystrokes match this binding.
80 pub fn match_keystrokes(&self, typed: &[Keystroke]) -> Option<bool> {
81 if self.keystrokes.len() < typed.len() {
82 return None;
83 }
84
85 for (target, typed) in self.keystrokes.iter().zip(typed.iter()) {
86 if !typed.should_match(target) {
87 return None;
88 }
89 }
90
91 Some(self.keystrokes.len() > typed.len())
92 }
93
94 /// Get the keystrokes associated with this binding
95 pub fn keystrokes(&self) -> &[Keystroke] {
96 self.keystrokes.as_slice()
97 }
98
99 /// Get the action associated with this binding
100 pub fn action(&self) -> &dyn Action {
101 self.action.as_ref()
102 }
103
104 /// Get the predicate used to match this binding
105 pub fn predicate(&self) -> Option<Rc<KeyBindingContextPredicate>> {
106 self.context_predicate.as_ref().map(|rc| rc.clone())
107 }
108
109 /// Get the metadata for this binding
110 pub fn meta(&self) -> Option<KeyBindingMetaIndex> {
111 self.meta
112 }
113}
114
115impl std::fmt::Debug for KeyBinding {
116 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117 f.debug_struct("KeyBinding")
118 .field("keystrokes", &self.keystrokes)
119 .field("context_predicate", &self.context_predicate)
120 .field("action", &self.action.name())
121 .finish()
122 }
123}
124
125/// A unique identifier for retrieval of metadata associated with a key binding.
126/// Intended to be used as an index or key into a user-defined store of metadata
127/// associated with the binding, such as the source of the binding.
128#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
129pub struct KeyBindingMetaIndex(pub u32);