use std::rc::Rc; use crate::{ Action, AsKeystroke, DummyKeyboardMapper, InvalidKeystrokeError, KeyBindingContextPredicate, KeybindingKeystroke, Keystroke, PlatformKeyboardMapper, SharedString, }; use smallvec::SmallVec; /// A keybinding and its associated metadata, from the keymap. pub struct KeyBinding { pub(crate) action: Box, pub(crate) keystrokes: SmallVec<[KeybindingKeystroke; 2]>, pub(crate) context_predicate: Option>, pub(crate) meta: Option, /// The json input string used when building the keybinding, if any pub(crate) action_input: Option, } impl Clone for KeyBinding { fn clone(&self) -> Self { KeyBinding { action: self.action.boxed_clone(), keystrokes: self.keystrokes.clone(), context_predicate: self.context_predicate.clone(), meta: self.meta, action_input: self.action_input.clone(), } } } impl KeyBinding { /// Construct a new keybinding from the given data. Panics on parse error. pub fn new(keystrokes: &str, action: A, context: Option<&str>) -> Self { let context_predicate = context.map(|context| KeyBindingContextPredicate::parse(context).unwrap().into()); Self::load( keystrokes, Box::new(action), context_predicate, false, None, &DummyKeyboardMapper, ) .unwrap() } /// Load a keybinding from the given raw data. pub fn load( keystrokes: &str, action: Box, context_predicate: Option>, use_key_equivalents: bool, action_input: Option, keyboard_mapper: &dyn PlatformKeyboardMapper, ) -> std::result::Result { let keystrokes: SmallVec<[KeybindingKeystroke; 2]> = keystrokes .split_whitespace() .map(|source| { let keystroke = Keystroke::parse(source)?; Ok(KeybindingKeystroke::new_with_mapper( keystroke, use_key_equivalents, keyboard_mapper, )) }) .collect::>()?; Ok(Self { keystrokes, action, context_predicate, meta: None, action_input, }) } /// Set the metadata for this binding. pub fn with_meta(mut self, meta: KeyBindingMetaIndex) -> Self { self.meta = Some(meta); self } /// Set the metadata for this binding. pub fn set_meta(&mut self, meta: KeyBindingMetaIndex) { self.meta = Some(meta); } /// Check if the given keystrokes match this binding. pub fn match_keystrokes(&self, typed: &[impl AsKeystroke]) -> Option { if self.keystrokes.len() < typed.len() { return None; } for (target, typed) in self.keystrokes.iter().zip(typed.iter()) { if !typed.as_keystroke().should_match(target) { return None; } } Some(self.keystrokes.len() > typed.len()) } /// Get the keystrokes associated with this binding pub fn keystrokes(&self) -> &[KeybindingKeystroke] { self.keystrokes.as_slice() } /// Get the action associated with this binding pub fn action(&self) -> &dyn Action { self.action.as_ref() } /// Get the predicate used to match this binding pub fn predicate(&self) -> Option> { self.context_predicate.as_ref().map(|rc| rc.clone()) } /// Get the metadata for this binding pub fn meta(&self) -> Option { self.meta } /// Get the action input associated with the action for this binding pub fn action_input(&self) -> Option { self.action_input.clone() } } impl std::fmt::Debug for KeyBinding { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("KeyBinding") .field("keystrokes", &self.keystrokes) .field("context_predicate", &self.context_predicate) .field("action", &self.action.name()) .finish() } } /// A unique identifier for retrieval of metadata associated with a key binding. /// Intended to be used as an index or key into a user-defined store of metadata /// associated with the binding, such as the source of the binding. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct KeyBindingMetaIndex(pub u32);