1use std::rc::Rc;
2
3use crate::{
4 Action, AsKeystroke, DummyKeyboardMapper, InvalidKeystrokeError, KeyBindingContextPredicate,
5 KeybindingKeystroke, Keystroke, PlatformKeyboardMapper, SharedString,
6};
7use smallvec::SmallVec;
8
9pub struct KeyBinding {
11 pub(crate) action: 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 pub(crate) action_input: Option<SharedString>,
17}
18
19impl Clone for KeyBinding {
20 fn clone(&self) -> Self {
21 KeyBinding {
22 action: self.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 pub fn new<A: Action>(keystrokes: &'static str, action: A, context: Option<&str>) -> Self {
36 let context_predicate =
37 context.map(|context| KeyBindingContextPredicate::parse(context).unwrap().into());
38 Self::load_internal(
39 keystrokes,
40 Box::new(action),
41 context_predicate,
42 false,
43 None,
44 &DummyKeyboardMapper,
45 Keystroke::parse,
46 )
47 .unwrap()
48 }
49
50 pub fn load(
52 keystrokes: &str,
53 action: Box<dyn Action>,
54 context_predicate: Option<Rc<KeyBindingContextPredicate>>,
55 use_key_equivalents: bool,
56 action_input: Option<SharedString>,
57 cx: &crate::App,
58 ) -> std::result::Result<Self, InvalidKeystrokeError> {
59 Self::load_internal(
60 keystrokes,
61 action,
62 context_predicate,
63 use_key_equivalents,
64 action_input,
65 cx.keyboard_mapper().as_ref(),
66 |source| Keystroke::parse_with_os(source, cx.platform_info().os),
67 )
68 }
69
70 fn load_internal<'a>(
71 keystrokes: &'a str,
72 action: Box<dyn Action>,
73 context_predicate: Option<Rc<KeyBindingContextPredicate>>,
74 use_key_equivalents: bool,
75 action_input: Option<SharedString>,
76 keyboard_mapper: &dyn PlatformKeyboardMapper,
77 parse_keystroke: impl Fn(&'a str) -> std::result::Result<Keystroke, InvalidKeystrokeError>,
78 ) -> std::result::Result<Self, InvalidKeystrokeError> {
79 let keystrokes: SmallVec<[KeybindingKeystroke; 2]> = keystrokes
80 .split_whitespace()
81 .map(|source| {
82 let keystroke = parse_keystroke(source)?;
83 Ok(KeybindingKeystroke::new_with_mapper(
84 keystroke,
85 use_key_equivalents,
86 keyboard_mapper,
87 ))
88 })
89 .collect::<std::result::Result<_, _>>()?;
90
91 Ok(Self {
92 keystrokes,
93 action,
94 context_predicate,
95 meta: None,
96 action_input,
97 })
98 }
99
100 pub fn with_meta(mut self, meta: KeyBindingMetaIndex) -> Self {
102 self.meta = Some(meta);
103 self
104 }
105
106 pub fn set_meta(&mut self, meta: KeyBindingMetaIndex) {
108 self.meta = Some(meta);
109 }
110
111 pub fn match_keystrokes(&self, typed: &[impl AsKeystroke]) -> Option<bool> {
113 if self.keystrokes.len() < typed.len() {
114 return None;
115 }
116
117 for (target, typed) in self.keystrokes.iter().zip(typed.iter()) {
118 if !typed.as_keystroke().should_match(target) {
119 return None;
120 }
121 }
122
123 Some(self.keystrokes.len() > typed.len())
124 }
125
126 pub fn keystrokes(&self) -> &[KeybindingKeystroke] {
128 self.keystrokes.as_slice()
129 }
130
131 pub fn action(&self) -> &dyn Action {
133 self.action.as_ref()
134 }
135
136 pub fn predicate(&self) -> Option<Rc<KeyBindingContextPredicate>> {
138 self.context_predicate.as_ref().map(|rc| rc.clone())
139 }
140
141 pub fn meta(&self) -> Option<KeyBindingMetaIndex> {
143 self.meta
144 }
145
146 pub fn action_input(&self) -> Option<SharedString> {
148 self.action_input.clone()
149 }
150}
151
152impl std::fmt::Debug for KeyBinding {
153 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154 f.debug_struct("KeyBinding")
155 .field("keystrokes", &self.keystrokes)
156 .field("context_predicate", &self.context_predicate)
157 .field("action", &self.action.name())
158 .finish()
159 }
160}
161
162#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
166pub struct KeyBindingMetaIndex(pub u32);