From a6c95ad331899bc0350f19923090982203e97998 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 13 Nov 2023 18:29:18 +0100 Subject: [PATCH] Fix panic when querying available actions --- crates/editor2/src/element.rs | 12 +- crates/gpui2/src/key_dispatch.rs | 189 +++++---------------- crates/gpui2/src/window.rs | 279 ++++++++++++++++++++++--------- 3 files changed, 249 insertions(+), 231 deletions(-) diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index 1b0f3c473c465f781f1fb1356e2ef5c82168e828..f8386ee271e658148d7edb9b32fec5c67a044443 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -15,12 +15,12 @@ use crate::{ use anyhow::Result; use collections::{BTreeMap, HashMap}; use gpui::{ - black, hsla, point, px, relative, size, transparent_black, Action, ActionListener, AnyElement, - AvailableSpace, BorrowAppContext, BorrowWindow, Bounds, ContentMask, Corners, DispatchPhase, - Edges, Element, ElementId, ElementInputHandler, Entity, FocusHandle, GlobalElementId, Hsla, - InputHandler, KeyContext, KeyDownEvent, KeyMatch, Line, LineLayout, Modifiers, MouseButton, - MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, ScrollWheelEvent, ShapedGlyph, Size, - Style, TextRun, TextStyle, TextSystem, ViewContext, WindowContext, WrappedLineLayout, + black, hsla, point, px, relative, size, transparent_black, Action, AnyElement, AvailableSpace, + BorrowAppContext, BorrowWindow, Bounds, ContentMask, Corners, DispatchPhase, Edges, Element, + ElementId, ElementInputHandler, Entity, FocusHandle, GlobalElementId, Hsla, InputHandler, + KeyContext, KeyDownEvent, KeyMatch, Line, LineLayout, Modifiers, MouseButton, MouseDownEvent, + MouseMoveEvent, MouseUpEvent, Pixels, ScrollWheelEvent, ShapedGlyph, Size, Style, TextRun, + TextStyle, TextSystem, ViewContext, WindowContext, WrappedLineLayout, }; use itertools::Itertools; use language::language_settings::ShowWhitespaceSetting; diff --git a/crates/gpui2/src/key_dispatch.rs b/crates/gpui2/src/key_dispatch.rs index b0f4a5d8d23680bb6b52fc3be22118175c977801..bc8e1f8f85b128c8178ba1ea234929c7cf1265bc 100644 --- a/crates/gpui2/src/key_dispatch.rs +++ b/crates/gpui2/src/key_dispatch.rs @@ -1,6 +1,6 @@ use crate::{ build_action_from_type, Action, Bounds, DispatchPhase, Element, FocusEvent, FocusHandle, - FocusId, KeyContext, KeyDownEvent, KeyMatch, Keymap, KeystrokeMatcher, MouseDownEvent, Pixels, + FocusId, KeyContext, KeyMatch, Keymap, Keystroke, KeystrokeMatcher, MouseDownEvent, Pixels, Style, StyleRefinement, ViewContext, WindowContext, }; use collections::HashMap; @@ -9,11 +9,11 @@ use refineable::Refineable; use smallvec::SmallVec; use std::{ any::{Any, TypeId}, + rc::Rc, sync::Arc, }; use util::ResultExt; -type KeyListener = Box; pub type FocusListeners = SmallVec<[FocusListener; 2]>; pub type FocusListener = Box) + 'static>; @@ -21,7 +21,7 @@ pub type FocusListener = #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub struct DispatchNodeId(usize); -pub struct KeyDispatcher { +pub(crate) struct DispatchTree { node_stack: Vec, context_stack: Vec, nodes: Vec, @@ -31,19 +31,22 @@ pub struct KeyDispatcher { } #[derive(Default)] -pub struct DispatchNode { - key_listeners: SmallVec<[KeyListener; 2]>, - action_listeners: SmallVec<[ActionListener; 16]>, - context: KeyContext, +pub(crate) struct DispatchNode { + pub key_listeners: SmallVec<[KeyListener; 2]>, + pub action_listeners: SmallVec<[ActionListener; 16]>, + pub context: KeyContext, parent: Option, } -struct ActionListener { - action_type: TypeId, - listener: Box, +type KeyListener = Rc; + +#[derive(Clone)] +pub(crate) struct ActionListener { + pub(crate) action_type: TypeId, + pub(crate) listener: Rc, } -impl KeyDispatcher { +impl DispatchTree { pub fn new(keymap: Arc>) -> Self { Self { node_stack: Vec::new(), @@ -97,7 +100,7 @@ impl KeyDispatcher { pub fn on_action( &mut self, action_type: TypeId, - listener: Box, + listener: Rc, ) { self.active_node().action_listeners.push(ActionListener { action_type, @@ -140,143 +143,40 @@ impl KeyDispatcher { actions } - pub fn dispatch_key(&mut self, target: FocusId, event: &dyn Any, cx: &mut WindowContext) { - if let Some(target_node_id) = self.focusable_node_ids.get(&target).copied() { - self.dispatch_key_on_node(target_node_id, event, cx); - } - } - - fn dispatch_key_on_node( + pub fn dispatch_key( &mut self, - node_id: DispatchNodeId, - event: &dyn Any, - cx: &mut WindowContext, - ) { - let dispatch_path = self.dispatch_path(node_id); - - // Capture phase - self.context_stack.clear(); - cx.propagate_event = true; - - for node_id in &dispatch_path { - let node = &self.nodes[node_id.0]; - if !node.context.is_empty() { - self.context_stack.push(node.context.clone()); - } - - for key_listener in &node.key_listeners { - key_listener(event, DispatchPhase::Capture, cx); - if !cx.propagate_event { - return; - } - } + keystroke: &Keystroke, + context: &[KeyContext], + ) -> Option> { + if !self + .keystroke_matchers + .contains_key(self.context_stack.as_slice()) + { + let keystroke_contexts = self.context_stack.iter().cloned().collect(); + self.keystroke_matchers.insert( + keystroke_contexts, + KeystrokeMatcher::new(self.keymap.clone()), + ); } - // Bubble phase - for node_id in dispatch_path.iter().rev() { - let node = &self.nodes[node_id.0]; - - // Handle low level key events - for key_listener in &node.key_listeners { - key_listener(event, DispatchPhase::Bubble, cx); - if !cx.propagate_event { - return; - } - } - - // Match keystrokes - if !node.context.is_empty() { - if let Some(key_down_event) = event.downcast_ref::() { - if !self - .keystroke_matchers - .contains_key(self.context_stack.as_slice()) - { - let keystroke_contexts = self.context_stack.iter().cloned().collect(); - self.keystroke_matchers.insert( - keystroke_contexts, - KeystrokeMatcher::new(self.keymap.clone()), - ); - } - - let keystroke_matcher = self - .keystroke_matchers - .get_mut(self.context_stack.as_slice()) - .unwrap(); - if let KeyMatch::Some(action) = keystroke_matcher - .match_keystroke(&key_down_event.keystroke, self.context_stack.as_slice()) - { - // Clear all pending keystrokes when an action has been found. - for keystroke_matcher in self.keystroke_matchers.values_mut() { - keystroke_matcher.clear_pending(); - } - - self.dispatch_action_on_node(*node_id, action, cx); - if !cx.propagate_event { - return; - } - } - } - - self.context_stack.pop(); + let keystroke_matcher = self + .keystroke_matchers + .get_mut(self.context_stack.as_slice()) + .unwrap(); + if let KeyMatch::Some(action) = keystroke_matcher.match_keystroke(keystroke, context) { + // Clear all pending keystrokes when an action has been found. + for keystroke_matcher in self.keystroke_matchers.values_mut() { + keystroke_matcher.clear_pending(); } - } - } - pub fn dispatch_action( - &self, - target: FocusId, - action: Box, - cx: &mut WindowContext, - ) { - if let Some(target_node_id) = self.focusable_node_ids.get(&target).copied() { - self.dispatch_action_on_node(target_node_id, action, cx); + Some(action) + } else { + None } } - fn dispatch_action_on_node( - &self, - node_id: DispatchNodeId, - action: Box, - cx: &mut WindowContext, - ) { - let dispatch_path = self.dispatch_path(node_id); - - // Capture phase - for node_id in &dispatch_path { - let node = &self.nodes[node_id.0]; - for ActionListener { - action_type, - listener, - } in &node.action_listeners - { - let any_action = action.as_any(); - if *action_type == any_action.type_id() { - listener(any_action, DispatchPhase::Capture, cx); - if !cx.propagate_event { - return; - } - } - } - } - - // Bubble phase - for node_id in dispatch_path.iter().rev() { - let node = &self.nodes[node_id.0]; - for ActionListener { - action_type, - listener, - } in &node.action_listeners - { - let any_action = action.as_any(); - if *action_type == any_action.type_id() { - cx.propagate_event = false; // Actions stop propagation by default during the bubble phase - listener(any_action, DispatchPhase::Bubble, cx); - if !cx.propagate_event { - return; - } - } - } - } + pub fn node(&self, node_id: DispatchNodeId) -> &DispatchNode { + &self.nodes[node_id.0] } fn active_node(&mut self) -> &mut DispatchNode { @@ -288,8 +188,7 @@ impl KeyDispatcher { *self.node_stack.last().unwrap() } - /// Returns the DispatchNodeIds from the root of the tree to the given target node id. - fn dispatch_path(&self, target: DispatchNodeId) -> SmallVec<[DispatchNodeId; 32]> { + pub fn dispatch_path(&self, target: DispatchNodeId) -> SmallVec<[DispatchNodeId; 32]> { let mut dispatch_path: SmallVec<[DispatchNodeId; 32]> = SmallVec::new(); let mut current_node_id = Some(target); while let Some(node_id) = current_node_id { @@ -299,6 +198,10 @@ impl KeyDispatcher { dispatch_path.reverse(); // Reverse the path so it goes from the root to the focused node. dispatch_path } + + pub fn focusable_node_id(&self, target: FocusId) -> Option { + self.focusable_node_ids.get(&target).copied() + } } pub trait KeyDispatch: 'static { diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 82d5982475f743c951f8b241c5b1d1fd870b82b5..f574d7eb5f5c6ee14031a99bd79aabd59bae1424 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -1,14 +1,15 @@ use crate::{ - px, size, Action, AnyBox, AnyDrag, AnyView, AppContext, AsyncWindowContext, AvailableSpace, - Bounds, BoxShadow, Context, Corners, CursorStyle, DevicePixels, DisplayId, Edges, Effect, - Entity, EntityId, EventEmitter, FileDropEvent, FocusEvent, FontId, GlobalElementId, GlyphId, - Hsla, ImageData, InputEvent, IsZero, KeyContext, KeyDispatcher, LayoutId, Model, ModelContext, - Modifiers, MonochromeSprite, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, - Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, - PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams, - RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, SubscriberSet, - Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext, - WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS, + key_dispatch::ActionListener, px, size, Action, AnyBox, AnyDrag, AnyView, AppContext, + AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle, + DevicePixels, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, + EventEmitter, FileDropEvent, FocusEvent, FontId, GlobalElementId, GlyphId, Hsla, ImageData, + InputEvent, IsZero, KeyContext, KeyDownEvent, LayoutId, Model, ModelContext, Modifiers, + MonochromeSprite, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, + PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, + PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, + SceneBuilder, Shadow, SharedString, Size, Style, SubscriberSet, Subscription, + TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext, WeakView, + WindowBounds, WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::{anyhow, Result}; use collections::HashMap; @@ -89,9 +90,7 @@ impl FocusId { pub(crate) fn contains(&self, other: Self, cx: &WindowContext) -> bool { cx.window .current_frame - .key_dispatcher - .as_ref() - .unwrap() + .dispatch_tree .focus_contains(*self, other) } } @@ -213,7 +212,7 @@ pub struct Window { pub(crate) struct Frame { element_states: HashMap, mouse_listeners: HashMap>, - pub(crate) key_dispatcher: Option, + pub(crate) dispatch_tree: DispatchTree, pub(crate) focus_listeners: Vec, pub(crate) scene_builder: SceneBuilder, z_index_stack: StackingOrder, @@ -222,11 +221,11 @@ pub(crate) struct Frame { } impl Frame { - pub fn new(key_dispatcher: KeyDispatcher) -> Self { + pub fn new(dispatch_tree: DispatchTree) -> Self { Frame { element_states: HashMap::default(), mouse_listeners: HashMap::default(), - key_dispatcher: Some(key_dispatcher), + dispatch_tree, focus_listeners: Vec::new(), scene_builder: SceneBuilder::default(), z_index_stack: StackingOrder::default(), @@ -302,8 +301,8 @@ impl Window { layout_engine: TaffyLayoutEngine::new(), root_view: None, element_id_stack: GlobalElementId::default(), - previous_frame: Frame::new(KeyDispatcher::new(cx.keymap.clone())), - current_frame: Frame::new(KeyDispatcher::new(cx.keymap.clone())), + previous_frame: Frame::new(DispatchTree::new(cx.keymap.clone())), + current_frame: Frame::new(DispatchTree::new(cx.keymap.clone())), focus_handles: Arc::new(RwLock::new(SlotMap::with_key())), focus_listeners: SubscriberSet::new(), default_prevented: true, @@ -423,9 +422,14 @@ impl<'a> WindowContext<'a> { pub fn dispatch_action(&mut self, action: Box) { if let Some(focus_handle) = self.focused() { self.defer(move |cx| { - let dispatcher = cx.window.current_frame.key_dispatcher.take().unwrap(); - dispatcher.dispatch_action(focus_handle.id, action, cx); - cx.window.current_frame.key_dispatcher = Some(dispatcher); + if let Some(node_id) = cx + .window + .current_frame + .dispatch_tree + .focusable_node_id(focus_handle.id) + { + cx.dispatch_action_on_node(node_id, action); + } }) } } @@ -723,12 +727,14 @@ impl<'a> WindowContext<'a> { &mut self, handler: impl Fn(&Event, DispatchPhase, &mut WindowContext) + 'static, ) { - let key_dispatcher = self.window.current_frame.key_dispatcher.as_mut().unwrap(); - key_dispatcher.on_key_event(Box::new(move |event, phase, cx| { - if let Some(event) = event.downcast_ref::() { - handler(event, phase, cx) - } - })); + self.window + .current_frame + .dispatch_tree + .on_key_event(Rc::new(move |event, phase, cx| { + if let Some(event) = event.downcast_ref::() { + handler(event, phase, cx) + } + })); } /// Register an action listener on the window for the current frame. The type of action @@ -742,10 +748,9 @@ impl<'a> WindowContext<'a> { action_type: TypeId, handler: impl Fn(&dyn Any, DispatchPhase, &mut WindowContext) + 'static, ) { - let key_dispatcher = self.window.current_frame.key_dispatcher.as_mut().unwrap(); - key_dispatcher.on_action( + self.window.current_frame.dispatch_tree.on_action( action_type, - Box::new(move |action, phase, cx| handler(action, phase, cx)), + Rc::new(move |action, phase, cx| handler(action, phase, cx)), ); } @@ -1110,7 +1115,7 @@ impl<'a> WindowContext<'a> { frame.element_states.clear(); frame.mouse_listeners.values_mut().for_each(Vec::clear); frame.focus_listeners.clear(); - frame.key_dispatcher.as_mut().map(KeyDispatcher::clear); + frame.dispatch_tree.clear(); } /// Dispatch a mouse or keyboard event on the window. @@ -1172,63 +1177,172 @@ impl<'a> WindowContext<'a> { }; if let Some(any_mouse_event) = event.mouse_event() { - if let Some(mut handlers) = self - .window - .current_frame - .mouse_listeners - .remove(&any_mouse_event.type_id()) - { - // Because handlers may add other handlers, we sort every time. - handlers.sort_by(|(a, _), (b, _)| a.cmp(b)); + self.dispatch_mouse_event(any_mouse_event); + } else if let Some(any_key_event) = event.keyboard_event() { + self.dispatch_key_event(any_key_event); + } - // Capture phase, events bubble from back to front. Handlers for this phase are used for - // special purposes, such as detecting events outside of a given Bounds. - for (_, handler) in &mut handlers { - handler(any_mouse_event, DispatchPhase::Capture, self); + !self.app.propagate_event + } + + fn dispatch_mouse_event(&mut self, event: &dyn Any) { + if let Some(mut handlers) = self + .window + .current_frame + .mouse_listeners + .remove(&event.type_id()) + { + // Because handlers may add other handlers, we sort every time. + handlers.sort_by(|(a, _), (b, _)| a.cmp(b)); + + // Capture phase, events bubble from back to front. Handlers for this phase are used for + // special purposes, such as detecting events outside of a given Bounds. + for (_, handler) in &mut handlers { + handler(event, DispatchPhase::Capture, self); + if !self.app.propagate_event { + break; + } + } + + // Bubble phase, where most normal handlers do their work. + if self.app.propagate_event { + for (_, handler) in handlers.iter_mut().rev() { + handler(event, DispatchPhase::Bubble, self); if !self.app.propagate_event { break; } } + } - // Bubble phase, where most normal handlers do their work. - if self.app.propagate_event { - for (_, handler) in handlers.iter_mut().rev() { - handler(any_mouse_event, DispatchPhase::Bubble, self); - if !self.app.propagate_event { - break; - } + if self.app.propagate_event && event.downcast_ref::().is_some() { + self.active_drag = None; + } + + // Just in case any handlers added new handlers, which is weird, but possible. + handlers.extend( + self.window + .current_frame + .mouse_listeners + .get_mut(&event.type_id()) + .into_iter() + .flat_map(|handlers| handlers.drain(..)), + ); + self.window + .current_frame + .mouse_listeners + .insert(event.type_id(), handlers); + } + } + + fn dispatch_key_event(&mut self, event: &dyn Any) { + if let Some(node_id) = self.window.focus.and_then(|focus_id| { + self.window + .current_frame + .dispatch_tree + .focusable_node_id(focus_id) + }) { + let dispatch_path = self + .window + .current_frame + .dispatch_tree + .dispatch_path(node_id); + + // Capture phase + let mut context_stack: SmallVec<[KeyContext; 16]> = SmallVec::new(); + self.propagate_event = true; + + for node_id in &dispatch_path { + let node = self.window.current_frame.dispatch_tree.node(*node_id); + + if !node.context.is_empty() { + context_stack.push(node.context.clone()); + } + + for key_listener in node.key_listeners.clone() { + key_listener(event, DispatchPhase::Capture, self); + if !self.propagate_event { + return; } } + } - if self.app.propagate_event - && any_mouse_event.downcast_ref::().is_some() - { - self.active_drag = None; + // Bubble phase + for node_id in dispatch_path.iter().rev() { + // Handle low level key events + let node = self.window.current_frame.dispatch_tree.node(*node_id); + for key_listener in node.key_listeners.clone() { + key_listener(event, DispatchPhase::Bubble, self); + if !self.propagate_event { + return; + } } - // Just in case any handlers added new handlers, which is weird, but possible. - handlers.extend( - self.window - .current_frame - .mouse_listeners - .get_mut(&any_mouse_event.type_id()) - .into_iter() - .flat_map(|handlers| handlers.drain(..)), - ); - self.window - .current_frame - .mouse_listeners - .insert(any_mouse_event.type_id(), handlers); + // Match keystrokes + let node = self.window.current_frame.dispatch_tree.node(*node_id); + if !node.context.is_empty() { + if let Some(key_down_event) = event.downcast_ref::() { + if let Some(action) = self + .window + .current_frame + .dispatch_tree + .dispatch_key(&key_down_event.keystroke, &context_stack) + { + self.dispatch_action_on_node(*node_id, action); + if !self.propagate_event { + return; + } + } + } + + context_stack.pop(); + } } - } else if let Some(any_key_event) = event.keyboard_event() { - if let Some(focus_id) = self.window.focus { - let mut dispatcher = self.window.current_frame.key_dispatcher.take().unwrap(); - dispatcher.dispatch_key(focus_id, any_key_event, self); - self.window.current_frame.key_dispatcher = Some(dispatcher); + } + } + + fn dispatch_action_on_node(&mut self, node_id: DispatchNodeId, action: Box) { + let dispatch_path = self + .window + .current_frame + .dispatch_tree + .dispatch_path(node_id); + + // Capture phase + for node_id in &dispatch_path { + let node = self.window.current_frame.dispatch_tree.node(*node_id); + for ActionListener { + action_type, + listener, + } in node.action_listeners.clone() + { + let any_action = action.as_any(); + if action_type == any_action.type_id() { + listener(any_action, DispatchPhase::Capture, self); + if !self.propagate_event { + return; + } + } } } - !self.app.propagate_event + // Bubble phase + for node_id in dispatch_path.iter().rev() { + let node = self.window.current_frame.dispatch_tree.node(*node_id); + for ActionListener { + action_type, + listener, + } in node.action_listeners.clone() + { + let any_action = action.as_any(); + if action_type == any_action.type_id() { + self.propagate_event = false; // Actions stop propagation by default during the bubble phase + listener(any_action, DispatchPhase::Bubble, self); + if !self.propagate_event { + return; + } + } + } + } } /// Register the given handler to be invoked whenever the global of the given type @@ -1261,9 +1375,7 @@ impl<'a> WindowContext<'a> { if let Some(focus_id) = self.window.focus { self.window .current_frame - .key_dispatcher - .as_ref() - .unwrap() + .dispatch_tree .available_actions(focus_id) } else { Vec::new() @@ -1926,17 +2038,20 @@ impl<'a, V: 'static> ViewContext<'a, V> { f: impl FnOnce(Option, &mut Self) -> R, ) -> R { let window = &mut self.window; - let old_dispatcher = window.previous_frame.key_dispatcher.as_mut().unwrap(); - let current_dispatcher = window.current_frame.key_dispatcher.as_mut().unwrap(); - current_dispatcher.push_node(context, old_dispatcher); + window + .current_frame + .dispatch_tree + .push_node(context, &mut window.previous_frame.dispatch_tree); if let Some(focus_handle) = focus_handle.as_ref() { - current_dispatcher.make_focusable(focus_handle.id); + window + .current_frame + .dispatch_tree + .make_focusable(focus_handle.id); } let result = f(focus_handle, self); - let current_dispatcher = self.window.current_frame.key_dispatcher.as_mut().unwrap(); - current_dispatcher.pop_node(); + self.window.current_frame.dispatch_tree.pop_node(); result }