diff --git a/crates/gpui3/src/events.rs b/crates/gpui3/src/events.rs index 7bd99d61697e6ec25391cb4e54a4548f658ca32f..8adfba37ada3cd9038378a8f421e352433b001ce 100644 --- a/crates/gpui3/src/events.rs +++ b/crates/gpui3/src/events.rs @@ -201,6 +201,19 @@ impl InputEvent { InputEvent::ScrollWheel(event) => Some(event), } } + + pub fn keyboard_event<'a>(&'a self) -> Option<&'a dyn Any> { + match self { + InputEvent::KeyDown(event) => Some(event), + InputEvent::KeyUp(event) => Some(event), + InputEvent::ModifiersChanged(event) => Some(event), + InputEvent::MouseDown(_) => None, + InputEvent::MouseUp(_) => None, + InputEvent::MouseMoved(_) => None, + InputEvent::MouseExited(_) => None, + InputEvent::ScrollWheel(_) => None, + } + } } pub struct FocusEvent { diff --git a/crates/gpui3/src/focus.rs b/crates/gpui3/src/focus.rs index bc9ebb984715c48551d6bf0bceb4ef6455fb6fc1..50338d36a903ca122ff3da9cf1a5ff5536c28588 100644 --- a/crates/gpui3/src/focus.rs +++ b/crates/gpui3/src/focus.rs @@ -1,4 +1,7 @@ -use crate::{Element, EventListeners, FocusEvent, FocusHandle, ViewContext}; +use crate::{ + DispatchPhase, Element, EventListeners, FocusEvent, FocusHandle, KeyDownEvent, KeyUpEvent, + ViewContext, +}; pub trait Focus: Element { fn handle(&self) -> &FocusHandle; @@ -95,4 +98,36 @@ pub trait Focus: Element { })); self } + + fn on_key_down( + mut self, + listener: impl Fn( + &mut Self::ViewState, + &KeyDownEvent, + DispatchPhase, + &mut ViewContext, + ) + Send + + Sync + + 'static, + ) -> Self + where + Self: Sized, + { + self.listeners().key_down.push(Box::new(listener)); + self + } + + fn on_key_up( + mut self, + listener: impl Fn(&mut Self::ViewState, &KeyUpEvent, DispatchPhase, &mut ViewContext) + + Send + + Sync + + 'static, + ) -> Self + where + Self: Sized, + { + self.listeners().key_up.push(Box::new(listener)); + self + } } diff --git a/crates/gpui3/src/platform.rs b/crates/gpui3/src/platform.rs index 1706ec14fe9bff02b0def88a71561742c3399503..5bb59acae03831bb37da3d72ed704f9c74c8d900 100644 --- a/crates/gpui3/src/platform.rs +++ b/crates/gpui3/src/platform.rs @@ -141,7 +141,7 @@ pub(crate) trait PlatformWindow { fn minimize(&self); fn zoom(&self); fn toggle_full_screen(&self); - fn on_event(&self, callback: Box bool>); + fn on_input(&self, callback: Box bool>); fn on_active_status_change(&self, callback: Box); fn on_resize(&self, callback: Box, f32)>); fn on_fullscreen(&self, callback: Box); diff --git a/crates/gpui3/src/platform/mac/window.rs b/crates/gpui3/src/platform/mac/window.rs index c73d59edde992b9031847721338a7e8867136007..519eab5b093a1a0e9fbaf6b9446522df689b1f12 100644 --- a/crates/gpui3/src/platform/mac/window.rs +++ b/crates/gpui3/src/platform/mac/window.rs @@ -854,7 +854,7 @@ impl PlatformWindow for MacWindow { .detach(); } - fn on_event(&self, callback: Box bool>) { + fn on_input(&self, callback: Box bool>) { self.0.as_ref().lock().event_callback = Some(callback); } diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 3e1ab39fa56c66374185a008c074d8b144eb22ba..a9c4dcadea09317643efbb33b8fbea742b804928 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -10,7 +10,7 @@ use crate::{ WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::Result; -use collections::{HashMap, HashSet}; +use collections::HashMap; use derive_more::{Deref, DerefMut}; use smallvec::SmallVec; use std::{ @@ -42,6 +42,8 @@ pub enum DispatchPhase { type AnyMouseEventListener = Box; +type AnyKeyboardEventListener = + Box; type AnyFocusListener = Box; type AnyKeyDownListener = Box; @@ -66,7 +68,8 @@ impl FocusHandle { } pub fn contains_focused(&self, cx: &WindowContext) -> bool { - cx.window.containing_focus.contains(&self.id) + cx.focused() + .map_or(false, |focused| self.contains(&focused, cx)) } pub fn within_focused(&self, cx: &WindowContext) -> bool { @@ -102,12 +105,10 @@ pub struct Window { z_index_stack: StackingOrder, content_mask_stack: Vec>, mouse_listeners: HashMap>, + keyboard_listeners: HashMap>, focus_stack: Vec, focus_parents_by_child: HashMap, - containing_focus: HashSet, pub(crate) focus_listeners: Vec, - key_down_listeners: Vec, - key_up_listeners: Vec, propagate_event: bool, mouse_position: Point, scale_factor: f32, @@ -149,7 +150,7 @@ impl Window { } })); - platform_window.on_event({ + platform_window.on_input({ let cx = cx.to_async(); Box::new(move |event| { cx.update_window(handle, |cx| cx.dispatch_event(event)) @@ -174,13 +175,11 @@ impl Window { element_states: HashMap::default(), z_index_stack: StackingOrder(SmallVec::new()), content_mask_stack: Vec::new(), + mouse_listeners: HashMap::default(), + keyboard_listeners: HashMap::default(), focus_stack: Vec::new(), focus_parents_by_child: HashMap::default(), - containing_focus: HashSet::default(), - mouse_listeners: HashMap::default(), focus_listeners: Vec::new(), - key_down_listeners: Vec::new(), - key_up_listeners: Vec::new(), propagate_event: true, mouse_position, scale_factor, @@ -415,6 +414,19 @@ impl<'a, 'w> WindowContext<'a, 'w> { )) } + pub fn on_keyboard_event( + &mut self, + handler: impl Fn(&Event, DispatchPhase, &mut WindowContext) + Send + Sync + 'static, + ) { + self.window + .keyboard_listeners + .entry(TypeId::of::()) + .or_default() + .push(Box::new(move |event: &dyn Any, phase, cx| { + handler(event.downcast_ref().unwrap(), phase, cx) + })) + } + pub fn mouse_position(&self) -> Point { self.window.mouse_position } @@ -761,9 +773,7 @@ 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_down_listeners.clear(); - window.key_up_listeners.clear(); - window.containing_focus.clear(); + window.keyboard_listeners.values_mut().for_each(Vec::clear); window.focus_parents_by_child.clear(); } @@ -819,6 +829,39 @@ impl<'a, 'w> WindowContext<'a, 'w> { .mouse_listeners .insert(any_mouse_event.type_id(), handlers); } + } else if let Some(any_keyboard_event) = event.keyboard_event() { + if let Some(mut handlers) = self + .window + .keyboard_listeners + .remove(&any_keyboard_event.type_id()) + { + for handler in &handlers { + handler(any_keyboard_event, DispatchPhase::Capture, self); + if !self.window.propagate_event { + break; + } + } + + if self.window.propagate_event { + for handler in handlers.iter().rev() { + handler(any_keyboard_event, DispatchPhase::Bubble, self); + if !self.window.propagate_event { + break; + } + } + } + + handlers.extend( + self.window + .keyboard_listeners + .get_mut(&any_keyboard_event.type_id()) + .into_iter() + .flat_map(|handlers| handlers.drain(..)), + ); + self.window + .keyboard_listeners + .insert(any_keyboard_event.type_id(), handlers); + } } true @@ -1138,7 +1181,8 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { })); } - if let Some(parent_frame) = window.focus_stack.last() { + let mut focus_stack = mem::take(&mut window.focus_stack); + if let Some(parent_frame) = focus_stack.last() { window .focus_parents_by_child .insert(focus_handle.id, parent_frame.handle.id); @@ -1170,20 +1214,20 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { .log_err(); })); } - window.focus_stack.push(frame); + focus_stack.push(frame); if Some(focus_handle.id) == window.focus { - for frame in &mut window.focus_stack { - window.containing_focus.insert(frame.handle.id); - window - .key_down_listeners - .extend(frame.key_down_listeners.drain(..)); - window - .key_up_listeners - .extend(frame.key_up_listeners.drain(..)); + for focus_frame in &mut focus_stack { + for listener in focus_frame.key_down_listeners.drain(..) { + self.window_cx.on_keyboard_event(listener); + } + for listener in focus_frame.key_up_listeners.drain(..) { + self.window_cx.on_keyboard_event(listener); + } } } + self.window.focus_stack = focus_stack; let result = f(self); self.window.focus_stack.pop(); result @@ -1232,6 +1276,18 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { }) }); } + + pub fn on_keyboard_event( + &mut self, + handler: impl Fn(&mut V, &Event, DispatchPhase, &mut ViewContext) + Send + Sync + 'static, + ) { + let handle = self.handle().upgrade(self).unwrap(); + self.window_cx.on_keyboard_event(move |event, phase, cx| { + handle.update(cx, |view, cx| { + handler(view, event, phase, cx); + }) + }); + } } impl<'a, 'w, S: EventEmitter + Send + Sync + 'static> ViewContext<'a, 'w, S> {