binding.rs

  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);