Checkpoint

Antonio Scandurra created

Change summary

crates/gpui3/src/elements/div.rs | 126 ++++++++++++++++-----------------
crates/gpui3/src/window.rs       | 110 ++++++++++++++++++-----------
2 files changed, 128 insertions(+), 108 deletions(-)

Detailed changes

crates/gpui3/src/elements/div.rs 🔗

@@ -375,10 +375,17 @@ where
         element_state: Option<Self::ElementState>,
         cx: &mut ViewContext<Self::ViewState>,
     ) -> Self::ElementState {
-        for child in &mut self.children {
-            child.initialize(view_state, cx);
-        }
-        element_state.unwrap_or_default()
+        cx.with_focus(
+            self.focusability.focus_handle().cloned(),
+            self.listeners.key_down.clone(),
+            self.listeners.key_up.clone(),
+            |cx| {
+                for child in &mut self.children {
+                    child.initialize(view_state, cx);
+                }
+                element_state.unwrap_or_default()
+            },
+        )
     }
 
     fn layout(
@@ -408,68 +415,57 @@ where
         cx: &mut ViewContext<Self::ViewState>,
     ) {
         self.with_element_id(cx, |this, cx| {
-            cx.with_key_listeners(
-                this.focusability.focus_handle().cloned(),
-                this.listeners.key_down.clone(),
-                this.listeners.key_up.clone(),
-                |cx| {
-                    if let Some(group) = this.group.clone() {
-                        cx.default_global::<GroupBounds>()
-                            .0
-                            .entry(group)
-                            .or_default()
-                            .push(bounds);
-                    }
+            if let Some(group) = this.group.clone() {
+                cx.default_global::<GroupBounds>()
+                    .0
+                    .entry(group)
+                    .or_default()
+                    .push(bounds);
+            }
 
-                    let hover_group_bounds = this
-                        .group_hover
-                        .as_ref()
-                        .and_then(|group_hover| group_bounds(&group_hover.group, cx));
-                    let active_group_bounds = this
-                        .group_active
-                        .as_ref()
-                        .and_then(|group_active| group_bounds(&group_active.group, cx));
-                    let style = this.compute_style(bounds, element_state, cx);
-                    let z_index = style.z_index.unwrap_or(0);
-
-                    // Paint background and event handlers.
-                    cx.stack(z_index, |cx| {
-                        cx.stack(0, |cx| {
-                            style.paint(bounds, cx);
-                            this.paint_hover_listeners(bounds, hover_group_bounds, cx);
-                            this.paint_active_listener(
-                                bounds,
-                                active_group_bounds,
-                                element_state.active_state.clone(),
-                                cx,
-                            );
-                            this.paint_event_listeners(
-                                bounds,
-                                element_state.pending_click.clone(),
-                                cx,
-                            );
-                        });
-
-                        cx.stack(1, |cx| {
-                            style.apply_text_style(cx, |cx| {
-                                style.apply_overflow(bounds, cx, |cx| {
-                                    for child in &mut this.children {
-                                        child.paint(view_state, None, cx);
-                                    }
-                                })
-                            })
-                        });
-                    });
-
-                    if let Some(group) = this.group.as_ref() {
-                        cx.default_global::<GroupBounds>()
-                            .0
-                            .get_mut(group)
-                            .unwrap()
-                            .pop();
-                    }
-                },
-            )
+            let hover_group_bounds = this
+                .group_hover
+                .as_ref()
+                .and_then(|group_hover| group_bounds(&group_hover.group, cx));
+            let active_group_bounds = this
+                .group_active
+                .as_ref()
+                .and_then(|group_active| group_bounds(&group_active.group, cx));
+            let style = this.compute_style(bounds, element_state, cx);
+            let z_index = style.z_index.unwrap_or(0);
+
+            // Paint background and event handlers.
+            cx.stack(z_index, |cx| {
+                cx.stack(0, |cx| {
+                    style.paint(bounds, cx);
+                    this.paint_hover_listeners(bounds, hover_group_bounds, cx);
+                    this.paint_active_listener(
+                        bounds,
+                        active_group_bounds,
+                        element_state.active_state.clone(),
+                        cx,
+                    );
+                    this.paint_event_listeners(bounds, element_state.pending_click.clone(), cx);
+                });
+
+                cx.stack(1, |cx| {
+                    style.apply_text_style(cx, |cx| {
+                        style.apply_overflow(bounds, cx, |cx| {
+                            for child in &mut this.children {
+                                child.paint(view_state, None, cx);
+                            }
+                        })
+                    })
+                });
+            });
+
+            if let Some(group) = this.group.as_ref() {
+                cx.default_global::<GroupBounds>()
+                    .0
+                    .get_mut(group)
+                    .unwrap()
+                    .pop();
+            }
         })
     }
 }

crates/gpui3/src/window.rs 🔗

@@ -9,7 +9,7 @@ use crate::{
     Task, Underline, UnderlineStyle, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
 };
 use anyhow::Result;
-use collections::HashMap;
+use collections::{HashMap, HashSet};
 use derive_more::{Deref, DerefMut};
 use smallvec::SmallVec;
 use std::{
@@ -42,7 +42,7 @@ pub enum DispatchPhase {
 type MouseEventHandler =
     Arc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) + Send + Sync + 'static>;
 
-#[derive(Copy, Clone, PartialEq, Eq)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
 pub struct FocusId(usize);
 
 #[derive(Clone)]
@@ -64,10 +64,13 @@ pub struct Window {
     element_states: HashMap<GlobalElementId, AnyBox>,
     z_index_stack: StackingOrder,
     content_mask_stack: Vec<ContentMask<Pixels>>,
-    mouse_event_handlers: HashMap<TypeId, Vec<(StackingOrder, MouseEventHandler)>>,
-    key_down_listener_stack:
+    mouse_listeners: HashMap<TypeId, Vec<(StackingOrder, MouseEventHandler)>>,
+    focus_stack: Vec<FocusStackFrame>,
+    focus_parents_by_child: HashMap<FocusId, FocusId>,
+    focused_in: HashSet<FocusId>,
+    key_down_listeners:
         Vec<Arc<dyn Fn(&KeyDownEvent, DispatchPhase, &mut WindowContext) + Send + Sync + 'static>>,
-    key_up_listener_stack:
+    key_up_listeners:
         Vec<Arc<dyn Fn(&KeyUpEvent, DispatchPhase, &mut WindowContext) + Send + Sync + 'static>>,
     propagate_event: bool,
     mouse_position: Point<Pixels>,
@@ -76,7 +79,6 @@ pub struct Window {
     pub(crate) dirty: bool,
     focus: Option<FocusId>,
     next_focus_id: FocusId,
-    painted_focused_element: bool,
 }
 
 impl Window {
@@ -135,9 +137,12 @@ impl Window {
             element_states: HashMap::default(),
             z_index_stack: StackingOrder(SmallVec::new()),
             content_mask_stack: Vec::new(),
-            mouse_event_handlers: HashMap::default(),
-            key_down_listener_stack: Vec::new(),
-            key_up_listener_stack: Vec::new(),
+            focus_stack: Vec::new(),
+            focus_parents_by_child: HashMap::default(),
+            focused_in: HashSet::default(),
+            mouse_listeners: HashMap::default(),
+            key_down_listeners: Vec::new(),
+            key_up_listeners: Vec::new(),
             propagate_event: true,
             mouse_position,
             scale_factor,
@@ -145,7 +150,6 @@ impl Window {
             dirty: true,
             focus: None,
             next_focus_id: FocusId(0),
-            painted_focused_element: false,
         }
     }
 }
@@ -169,6 +173,16 @@ impl ContentMask<Pixels> {
     }
 }
 
+struct FocusStackFrame {
+    handle: FocusHandle,
+    key_down_listeners: SmallVec<
+        [Arc<dyn Fn(&KeyDownEvent, DispatchPhase, &mut WindowContext) + Send + Sync + 'static>; 2],
+    >,
+    key_up_listeners: SmallVec<
+        [Arc<dyn Fn(&KeyUpEvent, DispatchPhase, &mut WindowContext) + Send + Sync + 'static>; 2],
+    >,
+}
+
 pub struct WindowContext<'a, 'w> {
     app: Reference<'a, AppContext>,
     pub(crate) window: Reference<'w, Window>,
@@ -323,7 +337,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
     ) {
         let order = self.window.z_index_stack.clone();
         self.window
-            .mouse_event_handlers
+            .mouse_listeners
             .entry(TypeId::of::<Event>())
             .or_default()
             .push((
@@ -675,12 +689,14 @@ impl<'a, 'w> WindowContext<'a, 'w> {
 
         // Clear mouse event listeners, because elements add new element listeners
         // when the upcoming frame is painted.
-        window
-            .mouse_event_handlers
-            .values_mut()
-            .for_each(Vec::clear);
+        window.mouse_listeners.values_mut().for_each(Vec::clear);
 
-        window.painted_focused_element = false;
+        // Clear focus state, because we determine what is focused when the new elements
+        // in the upcoming frame are initialized.
+        window.key_down_listeners.clear();
+        window.key_up_listeners.clear();
+        window.focused_in.clear();
+        window.focus_parents_by_child.clear();
     }
 
     fn end_frame(&mut self) {
@@ -695,7 +711,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
 
             if let Some(mut handlers) = self
                 .window
-                .mouse_event_handlers
+                .mouse_listeners
                 .remove(&any_mouse_event.type_id())
             {
                 // Because handlers may add other handlers, we sort every time.
@@ -726,13 +742,13 @@ impl<'a, 'w> WindowContext<'a, 'w> {
                 // Just in case any handlers added new handlers, which is weird, but possible.
                 handlers.extend(
                     self.window
-                        .mouse_event_handlers
+                        .mouse_listeners
                         .get_mut(&any_mouse_event.type_id())
                         .into_iter()
                         .flat_map(|handlers| handlers.drain(..)),
                 );
                 self.window
-                    .mouse_event_handlers
+                    .mouse_listeners
                     .insert(any_mouse_event.type_id(), handlers);
             }
         }
@@ -1030,7 +1046,7 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
         });
     }
 
-    pub fn with_key_listeners<R>(
+    pub fn with_focus<R>(
         &mut self,
         focus_handle: Option<FocusHandle>,
         key_down: impl IntoIterator<Item = KeyDownListener<V>>,
@@ -1040,20 +1056,25 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
         let Some(focus_handle) = focus_handle else {
             return f(self);
         };
-        let Some(focused_id) = self.window.focus else {
-            return f(self);
-        };
-        if self.window.painted_focused_element {
-            return f(self);
+
+        let handle = self.handle();
+        let window = &mut *self.window;
+        if let Some(parent_frame) = window.focus_stack.last() {
+            window
+                .focus_parents_by_child
+                .insert(focus_handle.id, parent_frame.handle.id);
         }
 
-        let prev_key_down_len = self.window.key_down_listener_stack.len();
-        let prev_key_up_len = self.window.key_up_listener_stack.len();
+        let mut frame = FocusStackFrame {
+            handle: focus_handle.clone(),
+            key_down_listeners: SmallVec::new(),
+            key_up_listeners: SmallVec::new(),
+        };
 
         for listener in key_down {
-            let handle = self.handle();
-            self.window
-                .key_down_listener_stack
+            let handle = handle.clone();
+            frame
+                .key_down_listeners
                 .push(Arc::new(move |event, phase, cx| {
                     handle
                         .update(cx, |view, cx| listener(view, event, phase, cx))
@@ -1061,9 +1082,9 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
                 }));
         }
         for listener in key_up {
-            let handle = self.handle();
-            self.window
-                .key_up_listener_stack
+            let handle = handle.clone();
+            frame
+                .key_up_listeners
                 .push(Arc::new(move |event, phase, cx| {
                     handle
                         .update(cx, |view, cx| listener(view, event, phase, cx))
@@ -1071,19 +1092,22 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
                 }));
         }
 
-        if focus_handle.id == focused_id {
-            self.window.painted_focused_element = true;
+        window.focus_stack.push(frame);
+
+        if Some(focus_handle.id) == window.focus {
+            for frame in &window.focus_stack {
+                window.focused_in.insert(frame.handle.id);
+                window
+                    .key_down_listeners
+                    .extend_from_slice(&frame.key_down_listeners);
+                window
+                    .key_up_listeners
+                    .extend_from_slice(&frame.key_up_listeners);
+            }
         }
 
         let result = f(self);
-
-        if focus_handle.id != focused_id {
-            self.window
-                .key_down_listener_stack
-                .truncate(prev_key_down_len);
-            self.window.key_up_listener_stack.truncate(prev_key_up_len);
-        }
-
+        self.window.focus_stack.pop();
         result
     }