From 8914b945777e6d1982a22dd5a9681d529c7c0ffa Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 18 Oct 2023 15:46:17 +0200 Subject: [PATCH] Checkpoint --- crates/gpui3/src/app.rs | 6 +- crates/gpui3/src/elements/div.rs | 1 + crates/gpui3/src/events.rs | 5 ++ crates/gpui3/src/focus.rs | 98 ++++++++++++++++++++++++++++++++ crates/gpui3/src/gpui3.rs | 2 + crates/gpui3/src/window.rs | 41 +++++++++---- 6 files changed, 139 insertions(+), 14 deletions(-) create mode 100644 crates/gpui3/src/focus.rs diff --git a/crates/gpui3/src/app.rs b/crates/gpui3/src/app.rs index e871a7fad6fda76e0c5181b7c58c739f0ec0bdd0..59c62f4c17c4b571726d5bb7012d657fd6674265 100644 --- a/crates/gpui3/src/app.rs +++ b/crates/gpui3/src/app.rs @@ -234,7 +234,7 @@ impl AppContext { fn apply_focus_changed(&mut self, window_id: WindowId, focused: Option) { self.update_window(window_id, |cx| { if cx.window.focus == focused { - let mut listeners = mem::take(&mut cx.window.focus_change_listeners); + let mut listeners = mem::take(&mut cx.window.focus_listeners); let focused = focused.map(FocusHandle::new); let blurred = cx.window.last_blur.unwrap().map(FocusHandle::new); let event = FocusEvent { focused, blurred }; @@ -242,8 +242,8 @@ impl AppContext { listener(&event, cx); } - listeners.extend(cx.window.focus_change_listeners.drain(..)); - cx.window.focus_change_listeners = listeners; + listeners.extend(cx.window.focus_listeners.drain(..)); + cx.window.focus_listeners = listeners; } }) .ok(); diff --git a/crates/gpui3/src/elements/div.rs b/crates/gpui3/src/elements/div.rs index 8bf748568e3e4aa4b248414dba31b5572bd6a86d..5d874eb79e87f7e1085056f0f8a1850357c095b9 100644 --- a/crates/gpui3/src/elements/div.rs +++ b/crates/gpui3/src/elements/div.rs @@ -379,6 +379,7 @@ where self.focusability.focus_handle().cloned(), mem::take(&mut self.listeners.key_down), mem::take(&mut self.listeners.key_up), + mem::take(&mut self.listeners.focus), |cx| { for child in &mut self.children { child.initialize(view_state, cx); diff --git a/crates/gpui3/src/events.rs b/crates/gpui3/src/events.rs index 624b32bc2035d62dcf9b54b780d990f353fcef77..7bd99d61697e6ec25391cb4e54a4548f658ca32f 100644 --- a/crates/gpui3/src/events.rs +++ b/crates/gpui3/src/events.rs @@ -243,6 +243,9 @@ pub type KeyDownListener = pub type KeyUpListener = Box) + Send + Sync + 'static>; +pub type FocusListener = + Box) + Send + Sync + 'static>; + pub struct EventListeners { pub mouse_down: SmallVec<[MouseDownListener; 2]>, pub mouse_up: SmallVec<[MouseUpListener; 2]>, @@ -251,6 +254,7 @@ pub struct EventListeners { pub scroll_wheel: SmallVec<[ScrollWheelListener; 2]>, pub key_down: SmallVec<[KeyDownListener; 2]>, pub key_up: SmallVec<[KeyUpListener; 2]>, + pub focus: SmallVec<[FocusListener; 2]>, } impl Default for EventListeners { @@ -263,6 +267,7 @@ impl Default for EventListeners { scroll_wheel: SmallVec::new(), key_down: SmallVec::new(), key_up: SmallVec::new(), + focus: SmallVec::new(), } } } diff --git a/crates/gpui3/src/focus.rs b/crates/gpui3/src/focus.rs new file mode 100644 index 0000000000000000000000000000000000000000..bc9ebb984715c48551d6bf0bceb4ef6455fb6fc1 --- /dev/null +++ b/crates/gpui3/src/focus.rs @@ -0,0 +1,98 @@ +use crate::{Element, EventListeners, FocusEvent, FocusHandle, ViewContext}; + +pub trait Focus: Element { + fn handle(&self) -> &FocusHandle; + fn listeners(&mut self) -> &mut EventListeners; + + fn on_focus( + mut self, + listener: impl Fn(&mut Self::ViewState, &FocusEvent, &mut ViewContext) + + Send + + Sync + + 'static, + ) -> Self + where + Self: Sized, + { + let handle = self.handle().clone(); + self.listeners() + .focus + .push(Box::new(move |view, event, cx| { + if event.focused.as_ref() == Some(&handle) { + listener(view, event, cx) + } + })); + self + } + + fn on_blur( + mut self, + listener: impl Fn(&mut Self::ViewState, &FocusEvent, &mut ViewContext) + + Send + + Sync + + 'static, + ) -> Self + where + Self: Sized, + { + let handle = self.handle().clone(); + self.listeners() + .focus + .push(Box::new(move |view, event, cx| { + if event.blurred.as_ref() == Some(&handle) { + listener(view, event, cx) + } + })); + self + } + + fn on_focus_in( + mut self, + listener: impl Fn(&mut Self::ViewState, &FocusEvent, &mut ViewContext) + + Send + + Sync + + 'static, + ) -> Self + where + Self: Sized, + { + let handle = self.handle().clone(); + self.listeners() + .focus + .push(Box::new(move |view, event, cx| { + if event + .focused + .as_ref() + .map_or(false, |focused| focused.contains(&handle, cx)) + { + listener(view, event, cx) + } + })); + self + } + + fn on_focus_out( + mut self, + listener: impl Fn(&mut Self::ViewState, &FocusEvent, &mut ViewContext) + + Send + + Sync + + 'static, + ) -> Self + where + Self: Sized, + { + let handle = self.handle().clone(); + self.listeners() + .focus + .push(Box::new(move |view, event, cx| { + if event + .blurred + .as_ref() + .map_or(false, |blurred| handle.contains(&blurred, cx)) + { + listener(view, event, cx) + } + })); + self + } +} diff --git a/crates/gpui3/src/gpui3.rs b/crates/gpui3/src/gpui3.rs index 8e841e6c2e499f2be38a2d5ddd9466c4cb64f0c7..caf265a60c8df828308c22524dab7d0eaa612e55 100644 --- a/crates/gpui3/src/gpui3.rs +++ b/crates/gpui3/src/gpui3.rs @@ -6,6 +6,7 @@ mod element; mod elements; mod events; mod executor; +mod focus; mod geometry; mod hover; mod image_cache; @@ -31,6 +32,7 @@ pub use element::*; pub use elements::*; pub use events::*; pub use executor::*; +pub use focus::*; pub use geometry::*; pub use gpui3_macros::*; pub use hover::*; diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 26bbc7df4347267302c8182f6b68f15e36dcd1b7..3e1ab39fa56c66374185a008c074d8b144eb22ba 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1,9 +1,9 @@ use crate::{ px, size, 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, KeyDownEvent, KeyDownListener, KeyUpEvent, KeyUpListener, LayoutId, - MainThread, MainThreadOnly, MonochromeSprite, MouseMoveEvent, Path, Pixels, Platform, + EventEmitter, FocusEvent, FocusListener, FontId, GlobalElementId, GlyphId, Handle, Hsla, + ImageData, InputEvent, IsZero, KeyDownEvent, KeyDownListener, KeyUpEvent, KeyUpListener, + 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, @@ -42,7 +42,7 @@ pub enum DispatchPhase { type AnyMouseEventListener = Box; -type AnyFocusChangeListener = Box; +type AnyFocusListener = Box; type AnyKeyDownListener = Box; type AnyKeyUpListener = @@ -51,7 +51,7 @@ type AnyKeyUpListener = #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct FocusId(usize); -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq)] pub struct FocusHandle { pub(crate) id: FocusId, } @@ -70,9 +70,14 @@ impl FocusHandle { } pub fn within_focused(&self, cx: &WindowContext) -> bool { - let mut ancestor = Some(self.id); + let focused = cx.focused(); + focused.map_or(false, |focused| focused.contains(self, cx)) + } + + pub(crate) fn contains(&self, other: &Self, cx: &WindowContext) -> bool { + let mut ancestor = Some(other.id); while let Some(ancestor_id) = ancestor { - if cx.window.focus == Some(ancestor_id) { + if self.id == ancestor_id { return true; } else { ancestor = cx.window.focus_parents_by_child.get(&ancestor_id).copied(); @@ -100,7 +105,7 @@ pub struct Window { focus_stack: Vec, focus_parents_by_child: HashMap, containing_focus: HashSet, - pub(crate) focus_change_listeners: Vec, + pub(crate) focus_listeners: Vec, key_down_listeners: Vec, key_up_listeners: Vec, propagate_event: bool, @@ -173,7 +178,7 @@ impl Window { focus_parents_by_child: HashMap::default(), containing_focus: HashSet::default(), mouse_listeners: HashMap::default(), - focus_change_listeners: Vec::new(), + focus_listeners: Vec::new(), key_down_listeners: Vec::new(), key_up_listeners: Vec::new(), propagate_event: true, @@ -235,6 +240,10 @@ impl<'a, 'w> WindowContext<'a, 'w> { FocusHandle { id } } + pub fn focused(&self) -> Option { + self.window.focus.map(|id| FocusHandle::new(id)) + } + pub fn focus(&mut self, handle: &FocusHandle) { if self.window.last_blur.is_none() { self.window.last_blur = Some(self.window.focus); @@ -751,7 +760,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_change_listeners.clear(); + window.focus_listeners.clear(); window.key_down_listeners.clear(); window.key_up_listeners.clear(); window.containing_focus.clear(); @@ -1110,6 +1119,7 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { focus_handle: Option, key_down: impl IntoIterator>, key_up: impl IntoIterator>, + focus: impl IntoIterator>, f: impl FnOnce(&mut Self) -> R, ) -> R { let Some(focus_handle) = focus_handle else { @@ -1118,6 +1128,16 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { let handle = self.handle(); let window = &mut *self.window; + + for listener in focus { + let handle = handle.clone(); + window.focus_listeners.push(Box::new(move |event, cx| { + handle + .update(cx, |view, cx| listener(view, event, cx)) + .log_err(); + })); + } + if let Some(parent_frame) = window.focus_stack.last() { window .focus_parents_by_child @@ -1150,7 +1170,6 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { .log_err(); })); } - window.focus_stack.push(frame); if Some(focus_handle.id) == window.focus {