binding.rs

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