157 lines
5.1 KiB
Rust
157 lines
5.1 KiB
Rust
use std::rc::Rc;
|
|
|
|
use collections::HashMap;
|
|
|
|
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<dyn Action>,
|
|
pub(crate) keystrokes: SmallVec<[KeybindingKeystroke; 2]>,
|
|
pub(crate) context_predicate: Option<Rc<KeyBindingContextPredicate>>,
|
|
pub(crate) meta: Option<KeyBindingMetaIndex>,
|
|
/// The json input string used when building the keybinding, if any
|
|
pub(crate) action_input: Option<SharedString>,
|
|
}
|
|
|
|
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<A: Action>(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,
|
|
None,
|
|
&DummyKeyboardMapper,
|
|
)
|
|
.unwrap()
|
|
}
|
|
|
|
/// Load a keybinding from the given raw data.
|
|
pub fn load(
|
|
keystrokes: &str,
|
|
action: Box<dyn Action>,
|
|
context_predicate: Option<Rc<KeyBindingContextPredicate>>,
|
|
use_key_equivalents: bool,
|
|
key_equivalents: Option<&HashMap<char, char>>,
|
|
action_input: Option<SharedString>,
|
|
keyboard_mapper: &dyn PlatformKeyboardMapper,
|
|
) -> std::result::Result<Self, InvalidKeystrokeError> {
|
|
let mut keystrokes: SmallVec<[Keystroke; 2]> = keystrokes
|
|
.split_whitespace()
|
|
.map(Keystroke::parse)
|
|
.collect::<std::result::Result<_, _>>()?;
|
|
|
|
if let Some(equivalents) = key_equivalents {
|
|
for keystroke in keystrokes.iter_mut() {
|
|
if keystroke.key.chars().count() == 1
|
|
&& let Some(key) = equivalents.get(&keystroke.key.chars().next().unwrap())
|
|
{
|
|
keystroke.key = key.to_string();
|
|
}
|
|
}
|
|
}
|
|
|
|
let keystrokes = keystrokes
|
|
.into_iter()
|
|
.map(|keystroke| {
|
|
KeybindingKeystroke::new(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<bool> {
|
|
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<Rc<KeyBindingContextPredicate>> {
|
|
self.context_predicate.as_ref().map(|rc| rc.clone())
|
|
}
|
|
|
|
/// Get the metadata for this binding
|
|
pub fn meta(&self) -> Option<KeyBindingMetaIndex> {
|
|
self.meta
|
|
}
|
|
|
|
/// Get the action input associated with the action for this binding
|
|
pub fn action_input(&self) -> Option<SharedString> {
|
|
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);
|