binding.rs

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