1use std::rc::Rc;
2
3use crate::{
4 Action, AsKeystroke, DummyKeyboardMapper, InvalidKeystrokeError, KeyBindingContextPredicate,
5 KeybindingKeystroke, Keystroke, PlatformKeyboardMapper, SharedString,
6};
7use smallvec::SmallVec;
8
9/// A keybinding and its associated metadata, from the keymap.
10pub struct KeyBinding {
11 pub(crate) action: Option<Box<dyn Action>>,
12 pub(crate) keystrokes: SmallVec<[KeybindingKeystroke; 2]>,
13 pub(crate) context_predicate: Option<Rc<KeyBindingContextPredicate>>,
14 pub(crate) meta: Option<KeyBindingMetaIndex>,
15 /// The json input string used when building the keybinding, if any
16 pub(crate) action_input: Option<SharedString>,
17}
18
19impl Clone for KeyBinding {
20 fn clone(&self) -> Self {
21 KeyBinding {
22 action: self.action.as_ref().map(|action| action.boxed_clone()),
23 keystrokes: self.keystrokes.clone(),
24 context_predicate: self.context_predicate.clone(),
25 meta: self.meta,
26 action_input: self.action_input.clone(),
27 }
28 }
29}
30
31impl KeyBinding {
32 /// Construct a new keybinding from the given data. Panics on parse error.
33 pub fn new<A: Action>(keystrokes: &str, action: A, context: Option<&str>) -> Self {
34 let context_predicate =
35 context.map(|context| KeyBindingContextPredicate::parse(context).unwrap().into());
36 Self::load(
37 keystrokes,
38 Some(Box::new(action) as Box<dyn Action>),
39 context_predicate,
40 false,
41 None,
42 &DummyKeyboardMapper,
43 )
44 .unwrap()
45 }
46
47 /// Construct a new keybinding from the given data with no action associated to it. Panics on parse error.
48 pub fn new_no_action(keystrokes: &str, context: Option<&str>) -> Self {
49 let context_predicate =
50 context.map(|context| KeyBindingContextPredicate::parse(context).unwrap().into());
51 Self::load(
52 keystrokes,
53 None,
54 context_predicate,
55 false,
56 None,
57 &DummyKeyboardMapper,
58 )
59 .unwrap()
60 }
61
62 /// Load a keybinding from the given raw data.
63 pub fn load(
64 keystrokes: &str,
65 action: impl Into<Option<Box<dyn Action>>>,
66 context_predicate: Option<Rc<KeyBindingContextPredicate>>,
67 use_key_equivalents: bool,
68 action_input: Option<SharedString>,
69 keyboard_mapper: &dyn PlatformKeyboardMapper,
70 ) -> std::result::Result<Self, InvalidKeystrokeError> {
71 let keystrokes: SmallVec<[KeybindingKeystroke; 2]> = keystrokes
72 .split_whitespace()
73 .map(|source| {
74 let keystroke = Keystroke::parse(source)?;
75 Ok(KeybindingKeystroke::new_with_mapper(
76 keystroke,
77 use_key_equivalents,
78 keyboard_mapper,
79 ))
80 })
81 .collect::<std::result::Result<_, _>>()?;
82
83 Ok(Self {
84 keystrokes,
85 action: action.into(),
86 context_predicate,
87 meta: None,
88 action_input,
89 })
90 }
91
92 /// Set the metadata for this binding.
93 pub fn with_meta(mut self, meta: KeyBindingMetaIndex) -> Self {
94 self.meta = Some(meta);
95 self
96 }
97
98 /// Set the metadata for this binding.
99 pub fn set_meta(&mut self, meta: KeyBindingMetaIndex) {
100 self.meta = Some(meta);
101 }
102
103 /// Check if the given keystrokes match this binding.
104 pub fn match_keystrokes(&self, typed: &[impl AsKeystroke]) -> Option<bool> {
105 if self.keystrokes.len() < typed.len() {
106 return None;
107 }
108
109 for (target, typed) in self.keystrokes.iter().zip(typed.iter()) {
110 if !typed.as_keystroke().should_match(target) {
111 return None;
112 }
113 }
114
115 Some(self.keystrokes.len() > typed.len())
116 }
117
118 /// Get the keystrokes associated with this binding
119 pub fn keystrokes(&self) -> &[KeybindingKeystroke] {
120 self.keystrokes.as_slice()
121 }
122
123 /// Get the action associated with this binding
124 pub fn action(&self) -> Option<&dyn Action> {
125 self.action.as_deref()
126 }
127
128 /// Get the predicate used to match this binding
129 pub fn predicate(&self) -> Option<Rc<KeyBindingContextPredicate>> {
130 self.context_predicate.as_ref().map(|rc| rc.clone())
131 }
132
133 /// Get the metadata for this binding
134 pub fn meta(&self) -> Option<KeyBindingMetaIndex> {
135 self.meta
136 }
137
138 /// Get the action input associated with the action for this binding
139 pub fn action_input(&self) -> Option<SharedString> {
140 self.action_input.clone()
141 }
142}
143
144impl std::fmt::Debug for KeyBinding {
145 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146 f.debug_struct("KeyBinding")
147 .field("keystrokes", &self.keystrokes)
148 .field("context_predicate", &self.context_predicate)
149 .field(
150 "action",
151 &self.action.as_deref().map_or("null", |it| it.name()),
152 )
153 .finish()
154 }
155}
156
157/// A unique identifier for retrieval of metadata associated with a key binding.
158/// Intended to be used as an index or key into a user-defined store of metadata
159/// associated with the binding, such as the source of the binding.
160#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
161pub struct KeyBindingMetaIndex(pub u32);