Refactor key dispatch to work in terms of bindings
This commit is contained in:
parent
6c82380232
commit
b8ed83a452
4 changed files with 48 additions and 100 deletions
|
@ -275,27 +275,31 @@ impl DispatchTree {
|
||||||
pub fn dispatch_key(
|
pub fn dispatch_key(
|
||||||
&mut self,
|
&mut self,
|
||||||
keystroke: &Keystroke,
|
keystroke: &Keystroke,
|
||||||
context: &[KeyContext],
|
dispatch_path: &SmallVec<[DispatchNodeId; 32]>,
|
||||||
) -> Vec<Box<dyn Action>> {
|
) -> SmallVec<[KeyBinding; 1]> {
|
||||||
if !self.keystroke_matchers.contains_key(context) {
|
let mut actions = SmallVec::new();
|
||||||
let keystroke_contexts = context.iter().cloned().collect();
|
|
||||||
self.keystroke_matchers.insert(
|
|
||||||
keystroke_contexts,
|
|
||||||
KeystrokeMatcher::new(self.keymap.clone()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let keystroke_matcher = self.keystroke_matchers.get_mut(context).unwrap();
|
let mut context_stack: SmallVec<[KeyContext; 4]> = SmallVec::new();
|
||||||
if let KeyMatch::Some(actions) = keystroke_matcher.match_keystroke(keystroke, context) {
|
for node_id in dispatch_path {
|
||||||
// Clear all pending keystrokes when an action has been found.
|
let node = self.node(*node_id);
|
||||||
for keystroke_matcher in self.keystroke_matchers.values_mut() {
|
|
||||||
keystroke_matcher.clear_pending();
|
if let Some(context) = node.context.clone() {
|
||||||
|
context_stack.push(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
actions
|
|
||||||
} else {
|
|
||||||
vec![]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while !context_stack.is_empty() {
|
||||||
|
let keystroke_matcher = self
|
||||||
|
.keystroke_matchers
|
||||||
|
.entry(context_stack.clone())
|
||||||
|
.or_insert_with(|| KeystrokeMatcher::new(self.keymap.clone()));
|
||||||
|
|
||||||
|
let mut matches = keystroke_matcher.match_keystroke(keystroke, &context_stack);
|
||||||
|
actions.append(&mut matches);
|
||||||
|
context_stack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
actions
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_pending_keystrokes(&self) -> bool {
|
pub fn has_pending_keystrokes(&self) -> bool {
|
||||||
|
|
|
@ -46,7 +46,7 @@ impl KeyBinding {
|
||||||
if self.keystrokes.as_ref().starts_with(pending_keystrokes) {
|
if self.keystrokes.as_ref().starts_with(pending_keystrokes) {
|
||||||
// If the binding is completed, push it onto the matches list
|
// If the binding is completed, push it onto the matches list
|
||||||
if self.keystrokes.as_ref().len() == pending_keystrokes.len() {
|
if self.keystrokes.as_ref().len() == pending_keystrokes.len() {
|
||||||
KeyMatch::Some(vec![self.action.boxed_clone()])
|
KeyMatch::Matched
|
||||||
} else {
|
} else {
|
||||||
KeyMatch::Pending
|
KeyMatch::Pending
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::{Action, KeyContext, Keymap, KeymapVersion, Keystroke};
|
use crate::{Action, KeyBinding, KeyContext, Keymap, KeymapVersion, Keystroke};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
use smallvec::SmallVec;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub struct KeystrokeMatcher {
|
pub struct KeystrokeMatcher {
|
||||||
|
@ -39,7 +40,7 @@ impl KeystrokeMatcher {
|
||||||
&mut self,
|
&mut self,
|
||||||
keystroke: &Keystroke,
|
keystroke: &Keystroke,
|
||||||
context_stack: &[KeyContext],
|
context_stack: &[KeyContext],
|
||||||
) -> KeyMatch {
|
) -> SmallVec<[KeyBinding; 1]> {
|
||||||
let keymap = self.keymap.lock();
|
let keymap = self.keymap.lock();
|
||||||
// Clear pending keystrokes if the keymap has changed since the last matched keystroke.
|
// Clear pending keystrokes if the keymap has changed since the last matched keystroke.
|
||||||
if keymap.version() != self.keymap_version {
|
if keymap.version() != self.keymap_version {
|
||||||
|
@ -48,7 +49,7 @@ impl KeystrokeMatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut pending_key = None;
|
let mut pending_key = None;
|
||||||
let mut found_actions = Vec::new();
|
let mut found = SmallVec::new();
|
||||||
|
|
||||||
for binding in keymap.bindings().rev() {
|
for binding in keymap.bindings().rev() {
|
||||||
if !keymap.binding_enabled(binding, context_stack) {
|
if !keymap.binding_enabled(binding, context_stack) {
|
||||||
|
@ -58,8 +59,8 @@ impl KeystrokeMatcher {
|
||||||
for candidate in keystroke.match_candidates() {
|
for candidate in keystroke.match_candidates() {
|
||||||
self.pending_keystrokes.push(candidate.clone());
|
self.pending_keystrokes.push(candidate.clone());
|
||||||
match binding.match_keystrokes(&self.pending_keystrokes) {
|
match binding.match_keystrokes(&self.pending_keystrokes) {
|
||||||
KeyMatch::Some(mut actions) => {
|
KeyMatch::Matched => {
|
||||||
found_actions.append(&mut actions);
|
found.push(binding.clone());
|
||||||
}
|
}
|
||||||
KeyMatch::Pending => {
|
KeyMatch::Pending => {
|
||||||
pending_key.get_or_insert(candidate);
|
pending_key.get_or_insert(candidate);
|
||||||
|
@ -70,16 +71,15 @@ impl KeystrokeMatcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !found_actions.is_empty() {
|
if !found.is_empty() {
|
||||||
self.pending_keystrokes.clear();
|
self.pending_keystrokes.clear();
|
||||||
return KeyMatch::Some(found_actions);
|
|
||||||
} else if let Some(pending_key) = pending_key {
|
} else if let Some(pending_key) = pending_key {
|
||||||
self.pending_keystrokes.push(pending_key);
|
self.pending_keystrokes.push(pending_key);
|
||||||
KeyMatch::Pending
|
|
||||||
} else {
|
} else {
|
||||||
self.pending_keystrokes.clear();
|
self.pending_keystrokes.clear();
|
||||||
KeyMatch::None
|
};
|
||||||
}
|
|
||||||
|
found
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,43 +87,7 @@ impl KeystrokeMatcher {
|
||||||
pub enum KeyMatch {
|
pub enum KeyMatch {
|
||||||
None,
|
None,
|
||||||
Pending,
|
Pending,
|
||||||
Some(Vec<Box<dyn Action>>),
|
Matched,
|
||||||
}
|
|
||||||
|
|
||||||
impl KeyMatch {
|
|
||||||
pub fn is_some(&self) -> bool {
|
|
||||||
matches!(self, KeyMatch::Some(_))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn matches(self) -> Option<Vec<Box<dyn Action>>> {
|
|
||||||
match self {
|
|
||||||
KeyMatch::Some(matches) => Some(matches),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for KeyMatch {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
match (self, other) {
|
|
||||||
(KeyMatch::None, KeyMatch::None) => true,
|
|
||||||
(KeyMatch::Pending, KeyMatch::Pending) => true,
|
|
||||||
(KeyMatch::Some(a), KeyMatch::Some(b)) => {
|
|
||||||
if a.len() != b.len() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (a, b) in a.iter().zip(b.iter()) {
|
|
||||||
if !a.partial_eq(b.as_ref()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -1784,44 +1784,24 @@ impl<'a> WindowContext<'a> {
|
||||||
.dispatch_tree
|
.dispatch_tree
|
||||||
.dispatch_path(node_id);
|
.dispatch_path(node_id);
|
||||||
|
|
||||||
let mut actions: Vec<Box<dyn Action>> = Vec::new();
|
if let Some(key_down_event) = event.downcast_ref::<KeyDownEvent>() {
|
||||||
|
let bindings = self
|
||||||
|
.window
|
||||||
|
.rendered_frame
|
||||||
|
.dispatch_tree
|
||||||
|
.dispatch_key(&key_down_event.keystroke, &dispatch_path);
|
||||||
|
|
||||||
let mut context_stack: SmallVec<[KeyContext; 16]> = SmallVec::new();
|
if !bindings.is_empty() {
|
||||||
for node_id in &dispatch_path {
|
self.clear_pending_keystrokes();
|
||||||
let node = self.window.rendered_frame.dispatch_tree.node(*node_id);
|
|
||||||
|
|
||||||
if let Some(context) = node.context.clone() {
|
|
||||||
context_stack.push(context);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for node_id in dispatch_path.iter().rev() {
|
self.propagate_event = true;
|
||||||
// Match keystrokes
|
for binding in bindings {
|
||||||
let node = self.window.rendered_frame.dispatch_tree.node(*node_id);
|
self.dispatch_action_on_node(node_id, binding.action.boxed_clone());
|
||||||
if node.context.is_some() {
|
if !self.propagate_event {
|
||||||
if let Some(key_down_event) = event.downcast_ref::<KeyDownEvent>() {
|
self.dispatch_keystroke_observers(event, Some(binding.action));
|
||||||
let mut new_actions = self
|
return;
|
||||||
.window
|
|
||||||
.rendered_frame
|
|
||||||
.dispatch_tree
|
|
||||||
.dispatch_key(&key_down_event.keystroke, &context_stack);
|
|
||||||
actions.append(&mut new_actions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context_stack.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !actions.is_empty() {
|
|
||||||
self.clear_pending_keystrokes();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.propagate_event = true;
|
|
||||||
for action in actions {
|
|
||||||
self.dispatch_action_on_node(node_id, action.boxed_clone());
|
|
||||||
if !self.propagate_event {
|
|
||||||
self.dispatch_keystroke_observers(event, Some(action));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue