@@ -121,6 +121,7 @@ pub(crate) struct Replay {
#[derive(Default, Debug)]
pub(crate) struct DispatchResult {
pub(crate) pending: SmallVec<[Keystroke; 1]>,
+ pub(crate) pending_has_binding: bool,
pub(crate) bindings: SmallVec<[KeyBinding; 1]>,
pub(crate) to_replay: SmallVec<[Replay; 1]>,
pub(crate) context_stack: Vec<KeyContext>,
@@ -480,6 +481,7 @@ impl DispatchTree {
if pending {
return DispatchResult {
pending: input,
+ pending_has_binding: !bindings.is_empty(),
context_stack,
..Default::default()
};
@@ -608,9 +610,11 @@ impl DispatchTree {
#[cfg(test)]
mod tests {
use crate::{
- self as gpui, Element, ElementId, GlobalElementId, InspectorElementId, LayoutId, Style,
+ self as gpui, DispatchResult, Element, ElementId, GlobalElementId, InspectorElementId,
+ Keystroke, LayoutId, Style,
};
use core::panic;
+ use smallvec::SmallVec;
use std::{cell::RefCell, ops::Range, rc::Rc};
use crate::{
@@ -676,6 +680,49 @@ mod tests {
assert!(keybinding[0].action.partial_eq(&TestAction))
}
+ #[test]
+ fn test_pending_has_binding_state() {
+ let bindings = vec![
+ KeyBinding::new("ctrl-b h", TestAction, None),
+ KeyBinding::new("space", TestAction, Some("ContextA")),
+ KeyBinding::new("space f g", TestAction, Some("ContextB")),
+ ];
+ let keymap = Rc::new(RefCell::new(Keymap::new(bindings)));
+ let mut registry = ActionRegistry::default();
+ registry.load_action::<TestAction>();
+ let mut tree = DispatchTree::new(keymap, Rc::new(registry));
+
+ type DispatchPath = SmallVec<[super::DispatchNodeId; 32]>;
+ fn dispatch(
+ tree: &mut DispatchTree,
+ pending: SmallVec<[Keystroke; 1]>,
+ key: &str,
+ path: &DispatchPath,
+ ) -> DispatchResult {
+ tree.dispatch_key(pending, Keystroke::parse(key).unwrap(), path)
+ }
+
+ let dispatch_path: DispatchPath = SmallVec::new();
+ let result = dispatch(&mut tree, SmallVec::new(), "ctrl-b", &dispatch_path);
+ assert_eq!(result.pending.len(), 1);
+ assert!(!result.pending_has_binding);
+
+ let result = dispatch(&mut tree, result.pending, "h", &dispatch_path);
+ assert_eq!(result.pending.len(), 0);
+ assert_eq!(result.bindings.len(), 1);
+ assert!(!result.pending_has_binding);
+
+ let node_id = tree.push_node();
+ tree.set_key_context(KeyContext::parse("ContextB").unwrap());
+ tree.pop_node();
+
+ let dispatch_path = tree.dispatch_path(node_id);
+ let result = dispatch(&mut tree, SmallVec::new(), "space", &dispatch_path);
+
+ assert_eq!(result.pending.len(), 1);
+ assert!(!result.pending_has_binding);
+ }
+
#[crate::test]
fn test_input_handler_pending(cx: &mut TestAppContext) {
#[derive(Clone)]
@@ -909,6 +909,7 @@ struct PendingInput {
keystrokes: SmallVec<[Keystroke; 1]>,
focus: Option<FocusId>,
timer: Option<Task<()>>,
+ needs_timeout: bool,
}
pub(crate) struct ElementStateBox {
@@ -3896,32 +3897,52 @@ impl Window {
}
if !match_result.pending.is_empty() {
+ currently_pending.timer.take();
currently_pending.keystrokes = match_result.pending;
currently_pending.focus = self.focus;
- currently_pending.timer = Some(self.spawn(cx, async move |cx| {
- cx.background_executor.timer(Duration::from_secs(1)).await;
- cx.update(move |window, cx| {
- let Some(currently_pending) = window
- .pending_input
- .take()
- .filter(|pending| pending.focus == window.focus)
- else {
- return;
- };
- let node_id = window.focus_node_id_in_rendered_frame(window.focus);
- let dispatch_path = window.rendered_frame.dispatch_tree.dispatch_path(node_id);
-
- let to_replay = window
- .rendered_frame
- .dispatch_tree
- .flush_dispatch(currently_pending.keystrokes, &dispatch_path);
+ let text_input_requires_timeout = event
+ .downcast_ref::<KeyDownEvent>()
+ .filter(|key_down| key_down.keystroke.key_char.is_some())
+ .and_then(|_| self.platform_window.take_input_handler())
+ .map_or(false, |mut input_handler| {
+ let accepts = input_handler.accepts_text_input(self, cx);
+ self.platform_window.set_input_handler(input_handler);
+ accepts
+ });
- window.pending_input_changed(cx);
- window.replay_pending_input(to_replay, cx)
- })
- .log_err();
- }));
+ currently_pending.needs_timeout |=
+ match_result.pending_has_binding || text_input_requires_timeout;
+
+ if currently_pending.needs_timeout {
+ currently_pending.timer = Some(self.spawn(cx, async move |cx| {
+ cx.background_executor.timer(Duration::from_secs(1)).await;
+ cx.update(move |window, cx| {
+ let Some(currently_pending) = window
+ .pending_input
+ .take()
+ .filter(|pending| pending.focus == window.focus)
+ else {
+ return;
+ };
+
+ let node_id = window.focus_node_id_in_rendered_frame(window.focus);
+ let dispatch_path =
+ window.rendered_frame.dispatch_tree.dispatch_path(node_id);
+
+ let to_replay = window
+ .rendered_frame
+ .dispatch_tree
+ .flush_dispatch(currently_pending.keystrokes, &dispatch_path);
+
+ window.pending_input_changed(cx);
+ window.replay_pending_input(to_replay, cx)
+ })
+ .log_err();
+ }));
+ } else {
+ currently_pending.timer = None;
+ }
self.pending_input = Some(currently_pending);
self.pending_input_changed(cx);
cx.propagate_event = false;