diff --git a/crates/gpui3/src/action.rs b/crates/gpui3/src/action.rs index 6a21aee95c34883c150ccbfb42a26ef449f82574..22546a1449e654149eecbe173e0db774a9a4928a 100644 --- a/crates/gpui3/src/action.rs +++ b/crates/gpui3/src/action.rs @@ -10,14 +10,14 @@ pub trait Action: Any + Send + Sync { } #[derive(Clone, Debug, Default, Eq, PartialEq)] -pub struct ActionContext { +pub struct DispatchContext { set: HashSet, map: HashMap, } -impl ActionContext { +impl DispatchContext { pub fn new() -> Self { - ActionContext { + DispatchContext { set: HashSet::default(), map: HashMap::default(), } @@ -68,7 +68,7 @@ impl ActionContextPredicate { } } - pub fn eval(&self, contexts: &[ActionContext]) -> bool { + pub fn eval(&self, contexts: &[&DispatchContext]) -> bool { let Some(context) = contexts.first() else { return false; }; diff --git a/crates/gpui3/src/elements/div.rs b/crates/gpui3/src/elements/div.rs index 356e783971dc26929cc60b897df0f409efcb7fa4..8b7a2f741f0938fd87ddcd59c5a37abb60da863d 100644 --- a/crates/gpui3/src/elements/div.rs +++ b/crates/gpui3/src/elements/div.rs @@ -435,11 +435,11 @@ where if let Some(global_id) = global_id { key_listeners.push(( TypeId::of::(), - Arc::new(move |_, key_down, phase, cx| { + Arc::new(move |_, key_down, context, phase, cx| { if phase == DispatchPhase::Bubble { let key_down = key_down.downcast_ref::().unwrap(); if let KeyMatch::Some(action) = - cx.match_keystroke(&global_id, &key_down.keystroke) + cx.match_keystroke(&global_id, &key_down.keystroke, context) { return Some(action); } diff --git a/crates/gpui3/src/events.rs b/crates/gpui3/src/events.rs index 10eeb688cc52bd5695ee436683df2e293e87ed57..e4e43f03e03b44bd8df2d3818a571cac56ff569d 100644 --- a/crates/gpui3/src/events.rs +++ b/crates/gpui3/src/events.rs @@ -1,6 +1,6 @@ use crate::{ - point, Action, Bounds, DispatchPhase, FocusHandle, Keystroke, Modifiers, Pixels, Point, - ViewContext, + point, Action, Bounds, DispatchContext, DispatchPhase, FocusHandle, Keystroke, Modifiers, + Pixels, Point, ViewContext, }; use smallvec::SmallVec; use std::{ @@ -256,7 +256,13 @@ pub type ScrollWheelListener = Arc< >; pub type KeyListener = Arc< - dyn Fn(&mut V, &dyn Any, DispatchPhase, &mut ViewContext) -> Option> + dyn Fn( + &mut V, + &dyn Any, + &[&DispatchContext], + DispatchPhase, + &mut ViewContext, + ) -> Option> + Send + Sync + 'static, diff --git a/crates/gpui3/src/interactive.rs b/crates/gpui3/src/interactive.rs index 9d1197419f114ac2ce1b807bb8cbb1d3ce9f14be..40e815ccb9f7d5585049ebc562e179c04afee966 100644 --- a/crates/gpui3/src/interactive.rs +++ b/crates/gpui3/src/interactive.rs @@ -160,7 +160,7 @@ pub trait Interactive: Element { { self.listeners().key.push(( TypeId::of::(), - Arc::new(move |view, event, phase, cx| { + Arc::new(move |view, event, _, phase, cx| { let event = event.downcast_ref().unwrap(); listener(view, event, phase, cx); None @@ -181,7 +181,7 @@ pub trait Interactive: Element { { self.listeners().key.push(( TypeId::of::(), - Arc::new(move |view, event, phase, cx| { + Arc::new(move |view, event, _, phase, cx| { let event = event.downcast_ref().unwrap(); listener(view, event, phase, cx); None @@ -202,7 +202,7 @@ pub trait Interactive: Element { { self.listeners().key.push(( TypeId::of::(), - Arc::new(move |view, event, phase, cx| { + Arc::new(move |view, event, _, phase, cx| { let event = event.downcast_ref().unwrap(); listener(view, event, phase, cx); None diff --git a/crates/gpui3/src/keymap/binding.rs b/crates/gpui3/src/keymap/binding.rs index 2860ef52e78f8e040b68301edc228a2158b30c71..27289d0c674df508c49c4f2ab9c80e8027075718 100644 --- a/crates/gpui3/src/keymap/binding.rs +++ b/crates/gpui3/src/keymap/binding.rs @@ -1,4 +1,4 @@ -use crate::{Action, ActionContext, ActionContextPredicate, KeyMatch, Keystroke}; +use crate::{Action, ActionContextPredicate, DispatchContext, KeyMatch, Keystroke}; use anyhow::Result; use smallvec::SmallVec; @@ -32,7 +32,7 @@ impl KeyBinding { }) } - pub fn matches_context(&self, contexts: &[ActionContext]) -> bool { + pub fn matches_context(&self, contexts: &[&DispatchContext]) -> bool { self.context_predicate .as_ref() .map(|predicate| predicate.eval(contexts)) @@ -42,7 +42,7 @@ impl KeyBinding { pub fn match_keystrokes( &self, pending_keystrokes: &[Keystroke], - contexts: &[ActionContext], + contexts: &[&DispatchContext], ) -> KeyMatch { if self.keystrokes.as_ref().starts_with(&pending_keystrokes) && self.matches_context(contexts) @@ -61,7 +61,7 @@ impl KeyBinding { pub fn keystrokes_for_action( &self, action: &dyn Action, - contexts: &[ActionContext], + contexts: &[&DispatchContext], ) -> Option> { if self.action.eq(action) && self.matches_context(contexts) { Some(self.keystrokes.clone()) diff --git a/crates/gpui3/src/keymap/matcher.rs b/crates/gpui3/src/keymap/matcher.rs index 1718a871f99f87553e1ad58c8b49f79b0e4f201f..494285aaea6b7e644d5f0addbdad4e471444a3cc 100644 --- a/crates/gpui3/src/keymap/matcher.rs +++ b/crates/gpui3/src/keymap/matcher.rs @@ -1,4 +1,4 @@ -use crate::{Action, ActionContext, Keymap, KeymapVersion, Keystroke}; +use crate::{Action, DispatchContext, Keymap, KeymapVersion, Keystroke}; use parking_lot::RwLock; use smallvec::SmallVec; use std::sync::Arc; @@ -44,7 +44,7 @@ impl KeyMatcher { pub fn match_keystroke( &mut self, keystroke: &Keystroke, - context_stack: &[ActionContext], + context_stack: &[&DispatchContext], ) -> KeyMatch { let keymap = self.keymap.read(); // Clear pending keystrokes if the keymap has changed since the last matched keystroke. @@ -86,7 +86,7 @@ impl KeyMatcher { pub fn keystrokes_for_action( &self, action: &dyn Action, - contexts: &[ActionContext], + contexts: &[&DispatchContext], ) -> Option> { self.keymap .read() diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 5a68e0dfd2a55fe343a57435084e1bd68fa4767a..d1af98b667c331213bf87dbd4b2d3033640af81b 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1,13 +1,13 @@ use crate::{ px, size, Action, AnyBox, AnyView, AppContext, AsyncWindowContext, AvailableSpace, - BorrowAppContext, Bounds, BoxShadow, Context, Corners, DevicePixels, DisplayId, Edges, Effect, - Element, EntityId, EventEmitter, FocusEvent, FontId, GlobalElementId, GlyphId, Handle, Hsla, - ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, Keystroke, LayoutId, - MainThread, MainThreadOnly, MonochromeSprite, MouseMoveEvent, Path, Pixels, Platform, - PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams, - RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, - Style, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, WeakHandle, - WindowOptions, SUBPIXEL_VARIANTS, + BorrowAppContext, Bounds, BoxShadow, Context, Corners, DevicePixels, DispatchContext, + DisplayId, Edges, Effect, Element, EntityId, EventEmitter, FocusEvent, FontId, GlobalElementId, + GlyphId, Handle, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, + Keystroke, LayoutId, MainThread, MainThreadOnly, MonochromeSprite, MouseMoveEvent, Path, + Pixels, Platform, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Reference, + RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, + SharedString, Size, Style, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, + WeakHandle, WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::Result; use collections::HashMap; @@ -47,7 +47,12 @@ pub enum DispatchPhase { type AnyListener = Arc; type AnyKeyListener = Arc< - dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) -> Option> + dyn Fn( + &dyn Any, + &[&DispatchContext], + DispatchPhase, + &mut WindowContext, + ) -> Option> + Send + Sync + 'static, @@ -155,8 +160,8 @@ pub struct Window { z_index_stack: StackingOrder, content_mask_stack: Vec>, mouse_listeners: HashMap>, - key_listeners: Vec<(TypeId, AnyKeyListener)>, - key_events_enabled: bool, + key_dispatch_stack: Vec, + freeze_key_dispatch_stack: bool, focus_stack: Vec, focus_parents_by_child: HashMap, pub(crate) focus_listeners: Vec, @@ -230,24 +235,32 @@ impl Window { z_index_stack: StackingOrder(SmallVec::new()), content_mask_stack: Vec::new(), mouse_listeners: HashMap::default(), - key_listeners: Vec::new(), - key_events_enabled: true, + key_dispatch_stack: Vec::new(), + freeze_key_dispatch_stack: false, focus_stack: Vec::new(), focus_parents_by_child: HashMap::default(), focus_listeners: Vec::new(), + focus_handles: Arc::new(RwLock::new(SlotMap::with_key())), propagate: true, default_prevented: true, mouse_position, scale_factor, scene_builder: SceneBuilder::new(), dirty: true, - focus_handles: Arc::new(RwLock::new(SlotMap::with_key())), last_blur: None, focus: None, } } } +enum KeyDispatchStackFrame { + Listener { + event_type: TypeId, + listener: AnyKeyListener, + }, + Context(DispatchContext), +} + #[derive(Clone, Debug, Default, PartialEq, Eq)] #[repr(C)] pub struct ContentMask { @@ -833,9 +846,9 @@ impl<'a, 'w> WindowContext<'a, 'w> { // Clear focus state, because we determine what is focused when the new elements // in the upcoming frame are initialized. window.focus_listeners.clear(); - window.key_listeners.clear(); + window.key_dispatch_stack.clear(); window.focus_parents_by_child.clear(); - window.key_events_enabled = true; + window.freeze_key_dispatch_stack = false; } fn dispatch_event(&mut self, event: InputEvent) -> bool { @@ -888,36 +901,67 @@ impl<'a, 'w> WindowContext<'a, 'w> { .insert(any_mouse_event.type_id(), handlers); } } else if let Some(any_key_event) = event.keyboard_event() { - let key_listeners = mem::take(&mut self.window.key_listeners); + let key_dispatch_stack = mem::take(&mut self.window.key_dispatch_stack); let key_event_type = any_key_event.type_id(); - - for (ix, (listener_event_type, listener)) in key_listeners.iter().enumerate() { - if key_event_type == *listener_event_type { - if let Some(action) = listener(any_key_event, DispatchPhase::Capture, self) { - self.dispatch_action(action, &key_listeners[..ix]); + let mut context_stack = SmallVec::<[&DispatchContext; 16]>::new(); + + for (ix, frame) in key_dispatch_stack.iter().enumerate() { + match frame { + KeyDispatchStackFrame::Listener { + event_type, + listener, + } => { + if key_event_type == *event_type { + if let Some(action) = listener( + any_key_event, + &context_stack, + DispatchPhase::Capture, + self, + ) { + self.dispatch_action(action, &key_dispatch_stack[..ix]); + } + if !self.window.propagate { + break; + } + } } - if !self.window.propagate { - break; + KeyDispatchStackFrame::Context(context) => { + context_stack.push(&context); } } } if self.window.propagate { - for (ix, (listener_event_type, listener)) in key_listeners.iter().enumerate().rev() - { - if key_event_type == *listener_event_type { - if let Some(action) = listener(any_key_event, DispatchPhase::Bubble, self) { - self.dispatch_action(action, &key_listeners[..ix]); - } + for (ix, frame) in key_dispatch_stack.iter().enumerate().rev() { + match frame { + KeyDispatchStackFrame::Listener { + event_type, + listener, + } => { + if key_event_type == *event_type { + if let Some(action) = listener( + any_key_event, + &context_stack, + DispatchPhase::Bubble, + self, + ) { + self.dispatch_action(action, &key_dispatch_stack[..ix]); + } - if !self.window.propagate { - break; + if !self.window.propagate { + break; + } + } + } + KeyDispatchStackFrame::Context(_) => { + context_stack.pop(); } } } } - self.window.key_listeners = key_listeners; + drop(context_stack); + self.window.key_dispatch_stack = key_dispatch_stack; } true @@ -927,13 +971,14 @@ impl<'a, 'w> WindowContext<'a, 'w> { &mut self, element_id: &GlobalElementId, keystroke: &Keystroke, + context_stack: &[&DispatchContext], ) -> KeyMatch { let key_match = self .window .key_matchers .get_mut(element_id) .unwrap() - .match_keystroke(keystroke, &[]); + .match_keystroke(keystroke, context_stack); if key_match.is_some() { for matcher in self.window.key_matchers.values_mut() { @@ -944,23 +989,39 @@ impl<'a, 'w> WindowContext<'a, 'w> { key_match } - fn dispatch_action(&mut self, action: Box, listeners: &[(TypeId, AnyKeyListener)]) { + fn dispatch_action( + &mut self, + action: Box, + dispatch_stack: &[KeyDispatchStackFrame], + ) { let action_type = action.as_any().type_id(); - for (event_type, listener) in listeners { - if action_type == *event_type { - listener(action.as_any(), DispatchPhase::Capture, self); - if !self.window.propagate { - break; + for stack_frame in dispatch_stack { + if let KeyDispatchStackFrame::Listener { + event_type, + listener, + } = stack_frame + { + if action_type == *event_type { + listener(action.as_any(), &[], DispatchPhase::Capture, self); + if !self.window.propagate { + break; + } } } } if self.window.propagate { - for (event_type, listener) in listeners.iter().rev() { - if action_type == *event_type { - listener(action.as_any(), DispatchPhase::Bubble, self); - if !self.window.propagate { - break; + for stack_frame in dispatch_stack.iter().rev() { + if let KeyDispatchStackFrame::Listener { + event_type, + listener, + } = stack_frame + { + if action_type == *event_type { + listener(action.as_any(), &[], DispatchPhase::Bubble, self); + if !self.window.propagate { + break; + } } } } @@ -1287,26 +1348,56 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { key_listeners: &[(TypeId, KeyListener)], f: impl FnOnce(&mut Self) -> R, ) -> R { - if self.window.key_events_enabled { + if !self.window.freeze_key_dispatch_stack { for (event_type, listener) in key_listeners.iter().cloned() { let handle = self.handle(); let listener = Arc::new( - move |event: &dyn Any, phase: DispatchPhase, cx: &mut WindowContext<'_, '_>| { + move |event: &dyn Any, + context_stack: &[&DispatchContext], + phase: DispatchPhase, + cx: &mut WindowContext<'_, '_>| { handle - .update(cx, |view, cx| listener(view, event, phase, cx)) + .update(cx, |view, cx| { + listener(view, event, context_stack, phase, cx) + }) .log_err() .flatten() }, ); - self.window.key_listeners.push((event_type, listener)); + self.window + .key_dispatch_stack + .push(KeyDispatchStackFrame::Listener { + event_type, + listener, + }); } } let result = f(self); - if self.window.key_events_enabled { - let prev_len = self.window.key_listeners.len() - key_listeners.len(); - self.window.key_listeners.truncate(prev_len); + if !self.window.freeze_key_dispatch_stack { + let prev_len = self.window.key_dispatch_stack.len() - key_listeners.len(); + self.window.key_dispatch_stack.truncate(prev_len); + } + + result + } + + pub fn with_key_dispatch_context( + &mut self, + context: DispatchContext, + f: impl FnOnce(&mut Self) -> R, + ) -> R { + if !self.window.freeze_key_dispatch_stack { + self.window + .key_dispatch_stack + .push(KeyDispatchStackFrame::Context(context)); + } + + let result = f(self); + + if !self.window.freeze_key_dispatch_stack { + self.window.key_dispatch_stack.pop(); } result @@ -1325,7 +1416,7 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { self.window.focus_stack.push(focus_handle.id); if Some(focus_handle.id) == self.window.focus { - self.window.key_events_enabled = false; + self.window.freeze_key_dispatch_stack = true; } let result = f(self);