Maintain view stack as part of `DispatchTree`

Antonio Scandurra created

Change summary

crates/gpui/src/key_dispatch.rs | 42 +++++++++++++++--
crates/gpui/src/scene.rs        | 79 ++++++++++++++++++--------------
crates/gpui/src/window.rs       | 83 ++++++++++++++++++++++++----------
3 files changed, 139 insertions(+), 65 deletions(-)

Detailed changes

crates/gpui/src/key_dispatch.rs 🔗

@@ -4,7 +4,7 @@ use crate::{
 };
 use collections::FxHashMap;
 use parking_lot::Mutex;
-use smallvec::SmallVec;
+use smallvec::{smallvec, SmallVec};
 use std::{
     any::{Any, TypeId},
     mem,
@@ -18,6 +18,7 @@ pub struct DispatchNodeId(usize);
 pub(crate) struct DispatchTree {
     node_stack: Vec<DispatchNodeId>,
     pub(crate) context_stack: Vec<KeyContext>,
+    view_stack: Vec<EntityId>,
     nodes: Vec<DispatchNode>,
     focusable_node_ids: FxHashMap<FocusId, DispatchNodeId>,
     view_node_ids: FxHashMap<EntityId, DispatchNodeId>,
@@ -49,6 +50,7 @@ impl DispatchTree {
         Self {
             node_stack: Vec::new(),
             context_stack: Vec::new(),
+            view_stack: Vec::new(),
             nodes: Vec::new(),
             focusable_node_ids: FxHashMap::default(),
             view_node_ids: FxHashMap::default(),
@@ -60,8 +62,9 @@ impl DispatchTree {
 
     pub fn clear(&mut self) {
         self.node_stack.clear();
-        self.nodes.clear();
         self.context_stack.clear();
+        self.view_stack.clear();
+        self.nodes.clear();
         self.focusable_node_ids.clear();
         self.view_node_ids.clear();
         self.keystroke_matchers.clear();
@@ -82,10 +85,14 @@ impl DispatchTree {
     }
 
     pub fn pop_node(&mut self) {
-        let node_id = self.node_stack.pop().unwrap();
-        if self.nodes[node_id.0].context.is_some() {
+        let node = &self.nodes[self.active_node_id().0];
+        if node.context.is_some() {
             self.context_stack.pop();
         }
+        if node.view_id.is_some() {
+            self.view_stack.pop();
+        }
+        self.node_stack.pop();
     }
 
     fn move_node(&mut self, source_node: &mut DispatchNode) {
@@ -102,7 +109,7 @@ impl DispatchTree {
         target_node.action_listeners = mem::take(&mut source_node.action_listeners);
     }
 
-    pub fn graft(&mut self, view_id: EntityId, source: &mut Self) {
+    pub fn graft(&mut self, view_id: EntityId, source: &mut Self) -> SmallVec<[EntityId; 8]> {
         let view_source_node_id = source
             .view_node_ids
             .get(&view_id)
@@ -110,6 +117,7 @@ impl DispatchTree {
         let view_source_node = &mut source.nodes[view_source_node_id.0];
         self.move_node(view_source_node);
 
+        let mut grafted_view_ids = smallvec![view_id];
         let mut source_stack = vec![*view_source_node_id];
         for (source_node_id, source_node) in source
             .nodes
@@ -130,12 +138,17 @@ impl DispatchTree {
             } else {
                 source_stack.push(source_node_id);
                 self.move_node(source_node);
+                if let Some(view_id) = source_node.view_id {
+                    grafted_view_ids.push(view_id);
+                }
             }
         }
 
         while !source_stack.is_empty() {
             self.pop_node();
         }
+
+        grafted_view_ids
     }
 
     pub fn clear_pending_keystrokes(&mut self) {
@@ -192,6 +205,7 @@ impl DispatchTree {
         let node_id = self.active_node_id();
         self.active_node().view_id = Some(view_id);
         self.view_node_ids.insert(view_id, node_id);
+        self.view_stack.push(view_id);
     }
 
     pub fn focus_contains(&self, parent: FocusId, child: FocusId) -> bool {
@@ -322,6 +336,24 @@ impl DispatchTree {
         focus_path
     }
 
+    pub fn view_path(&self, view_id: EntityId) -> SmallVec<[EntityId; 8]> {
+        let mut view_path: SmallVec<[EntityId; 8]> = SmallVec::new();
+        let mut current_node_id = self.view_node_ids.get(&view_id).copied();
+        while let Some(node_id) = current_node_id {
+            let node = self.node(node_id);
+            if let Some(view_id) = node.view_id {
+                view_path.push(view_id);
+            }
+            current_node_id = node.parent;
+        }
+        view_path.reverse(); // Reverse the path so it goes from the root to the view node.
+        view_path
+    }
+
+    pub fn active_view_id(&self) -> Option<EntityId> {
+        self.view_stack.last().copied()
+    }
+
     pub fn node(&self, node_id: DispatchNodeId) -> &DispatchNode {
         &self.nodes[node_id.0]
     }

crates/gpui/src/scene.rs 🔗

@@ -11,13 +11,12 @@ pub(crate) type PointF = Point<f32>;
 pub(crate) type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
 
 pub type LayerId = u32;
-
 pub type DrawOrder = u32;
 
 #[derive(Default)]
 pub(crate) struct SceneBuilder {
-    last_order: Option<(StackingOrder, LayerId)>,
     layers_by_order: BTreeMap<StackingOrder, LayerId>,
+    orders_by_layer: BTreeMap<LayerId, StackingOrder>,
     shadows: Vec<Shadow>,
     quads: Vec<Quad>,
     paths: Vec<Path<ScaledPixels>>,
@@ -34,40 +33,39 @@ impl SceneBuilder {
             orders[*layer_id as usize] = ix as u32;
         }
         self.layers_by_order.clear();
-        self.last_order = None;
 
         for shadow in &mut self.shadows {
-            shadow.order = orders[shadow.order as usize];
+            shadow.order = orders[shadow.layer_id as usize];
         }
         self.shadows.sort_by_key(|shadow| shadow.order);
 
         for quad in &mut self.quads {
-            quad.order = orders[quad.order as usize];
+            quad.order = orders[quad.layer_id as usize];
         }
         self.quads.sort_by_key(|quad| quad.order);
 
         for path in &mut self.paths {
-            path.order = orders[path.order as usize];
+            path.order = orders[path.layer_id as usize];
         }
         self.paths.sort_by_key(|path| path.order);
 
         for underline in &mut self.underlines {
-            underline.order = orders[underline.order as usize];
+            underline.order = orders[underline.layer_id as usize];
         }
         self.underlines.sort_by_key(|underline| underline.order);
 
         for monochrome_sprite in &mut self.monochrome_sprites {
-            monochrome_sprite.order = orders[monochrome_sprite.order as usize];
+            monochrome_sprite.order = orders[monochrome_sprite.layer_id as usize];
         }
         self.monochrome_sprites.sort_by_key(|sprite| sprite.order);
 
         for polychrome_sprite in &mut self.polychrome_sprites {
-            polychrome_sprite.order = orders[polychrome_sprite.order as usize];
+            polychrome_sprite.order = orders[polychrome_sprite.layer_id as usize];
         }
         self.polychrome_sprites.sort_by_key(|sprite| sprite.order);
 
         for surface in &mut self.surfaces {
-            surface.order = orders[surface.order as usize];
+            surface.order = orders[surface.layer_id as usize];
         }
         self.surfaces.sort_by_key(|surface| surface.order);
 
@@ -96,53 +94,46 @@ impl SceneBuilder {
         let layer_id = self.layer_id_for_order(order);
         match primitive {
             Primitive::Shadow(mut shadow) => {
-                shadow.order = layer_id;
+                shadow.layer_id = layer_id;
                 self.shadows.push(shadow);
             }
             Primitive::Quad(mut quad) => {
-                quad.order = layer_id;
+                quad.layer_id = layer_id;
                 self.quads.push(quad);
             }
             Primitive::Path(mut path) => {
-                path.order = layer_id;
+                path.layer_id = layer_id;
                 path.id = PathId(self.paths.len());
                 self.paths.push(path);
             }
             Primitive::Underline(mut underline) => {
-                underline.order = layer_id;
+                underline.layer_id = layer_id;
                 self.underlines.push(underline);
             }
             Primitive::MonochromeSprite(mut sprite) => {
-                sprite.order = layer_id;
+                sprite.layer_id = layer_id;
                 self.monochrome_sprites.push(sprite);
             }
             Primitive::PolychromeSprite(mut sprite) => {
-                sprite.order = layer_id;
+                sprite.layer_id = layer_id;
                 self.polychrome_sprites.push(sprite);
             }
             Primitive::Surface(mut surface) => {
-                surface.order = layer_id;
+                surface.layer_id = layer_id;
                 self.surfaces.push(surface);
             }
         }
     }
 
-    fn layer_id_for_order(&mut self, order: &StackingOrder) -> u32 {
-        if let Some((last_order, last_layer_id)) = self.last_order.as_ref() {
-            if last_order == order {
-                return *last_layer_id;
-            }
-        };
-
-        let layer_id = if let Some(layer_id) = self.layers_by_order.get(order) {
+    fn layer_id_for_order(&mut self, order: &StackingOrder) -> LayerId {
+        if let Some(layer_id) = self.layers_by_order.get(order) {
             *layer_id
         } else {
             let next_id = self.layers_by_order.len() as LayerId;
             self.layers_by_order.insert(order.clone(), next_id);
+            self.orders_by_layer.insert(next_id, order.clone());
             next_id
-        };
-        self.last_order = Some((order.clone(), layer_id));
-        layer_id
+        }
     }
 }
 
@@ -439,7 +430,9 @@ pub(crate) enum PrimitiveBatch<'a> {
 #[derive(Default, Debug, Clone, Eq, PartialEq)]
 #[repr(C)]
 pub struct Quad {
-    pub order: u32, // Initially a LayerId, then a DrawOrder.
+    pub view_id: u32,
+    pub layer_id: LayerId,
+    pub order: DrawOrder,
     pub bounds: Bounds<ScaledPixels>,
     pub content_mask: ContentMask<ScaledPixels>,
     pub background: Hsla,
@@ -469,7 +462,9 @@ impl From<Quad> for Primitive {
 #[derive(Debug, Clone, Eq, PartialEq)]
 #[repr(C)]
 pub struct Underline {
-    pub order: u32,
+    pub view_id: u32,
+    pub layer_id: LayerId,
+    pub order: DrawOrder,
     pub bounds: Bounds<ScaledPixels>,
     pub content_mask: ContentMask<ScaledPixels>,
     pub thickness: ScaledPixels,
@@ -498,7 +493,9 @@ impl From<Underline> for Primitive {
 #[derive(Debug, Clone, Eq, PartialEq)]
 #[repr(C)]
 pub struct Shadow {
-    pub order: u32,
+    pub view_id: u32,
+    pub layer_id: LayerId,
+    pub order: DrawOrder,
     pub bounds: Bounds<ScaledPixels>,
     pub corner_radii: Corners<ScaledPixels>,
     pub content_mask: ContentMask<ScaledPixels>,
@@ -527,7 +524,9 @@ impl From<Shadow> for Primitive {
 #[derive(Clone, Debug, Eq, PartialEq)]
 #[repr(C)]
 pub struct MonochromeSprite {
-    pub order: u32,
+    pub view_id: u32,
+    pub layer_id: LayerId,
+    pub order: DrawOrder,
     pub bounds: Bounds<ScaledPixels>,
     pub content_mask: ContentMask<ScaledPixels>,
     pub color: Hsla,
@@ -558,7 +557,9 @@ impl From<MonochromeSprite> for Primitive {
 #[derive(Clone, Debug, Eq, PartialEq)]
 #[repr(C)]
 pub struct PolychromeSprite {
-    pub order: u32,
+    pub view_id: u32,
+    pub layer_id: LayerId,
+    pub order: DrawOrder,
     pub bounds: Bounds<ScaledPixels>,
     pub content_mask: ContentMask<ScaledPixels>,
     pub corner_radii: Corners<ScaledPixels>,
@@ -589,7 +590,9 @@ impl From<PolychromeSprite> for Primitive {
 
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct Surface {
-    pub order: u32,
+    pub view_id: u32,
+    pub layer_id: LayerId,
+    pub order: DrawOrder,
     pub bounds: Bounds<ScaledPixels>,
     pub content_mask: ContentMask<ScaledPixels>,
     pub image_buffer: media::core_video::CVImageBuffer,
@@ -619,7 +622,9 @@ pub(crate) struct PathId(pub(crate) usize);
 #[derive(Debug)]
 pub struct Path<P: Clone + Default + Debug> {
     pub(crate) id: PathId,
-    order: u32,
+    pub(crate) view_id: u32,
+    layer_id: LayerId,
+    order: DrawOrder,
     pub(crate) bounds: Bounds<P>,
     pub(crate) content_mask: ContentMask<P>,
     pub(crate) vertices: Vec<PathVertex<P>>,
@@ -633,6 +638,8 @@ impl Path<Pixels> {
     pub fn new(start: Point<Pixels>) -> Self {
         Self {
             id: PathId(0),
+            view_id: 0,
+            layer_id: 0,
             order: 0,
             vertices: Vec::new(),
             start,
@@ -650,6 +657,8 @@ impl Path<Pixels> {
     pub fn scale(&self, factor: f32) -> Path<ScaledPixels> {
         Path {
             id: self.id,
+            view_id: self.view_id,
+            layer_id: self.layer_id,
             order: self.order,
             bounds: self.bounds.scale(factor),
             content_mask: self.content_mask.scale(factor),

crates/gpui/src/window.rs 🔗

@@ -295,8 +295,6 @@ pub(crate) struct Frame {
     pub(crate) next_stacking_order_id: u32,
     content_mask_stack: Vec<ContentMask<Pixels>>,
     element_offset_stack: Vec<Point<Pixels>>,
-    pub(crate) view_parents: FxHashMap<EntityId, EntityId>,
-    pub(crate) view_stack: Vec<EntityId>,
     pub(crate) reused_views: FxHashSet<EntityId>,
 }
 
@@ -313,8 +311,6 @@ impl Frame {
             depth_map: Default::default(),
             content_mask_stack: Vec::new(),
             element_offset_stack: Vec::new(),
-            view_parents: FxHashMap::default(),
-            view_stack: Vec::new(),
             reused_views: FxHashSet::default(),
         }
     }
@@ -325,8 +321,6 @@ impl Frame {
         self.dispatch_tree.clear();
         self.depth_map.clear();
         self.next_stacking_order_id = 0;
-        self.view_parents.clear();
-        debug_assert!(self.view_stack.is_empty());
         self.reused_views.clear();
     }
 
@@ -886,8 +880,8 @@ impl<'a> WindowContext<'a> {
         &mut self,
         mut handler: impl FnMut(&Event, DispatchPhase, &mut WindowContext) + 'static,
     ) {
+        let view_id = self.active_view_id();
         let order = self.window.next_frame.z_index_stack.clone();
-        let view_id = *self.window.next_frame.view_stack.last().unwrap();
         self.window
             .next_frame
             .mouse_listeners
@@ -1029,6 +1023,7 @@ impl<'a> WindowContext<'a> {
     ) {
         let scale_factor = self.scale_factor();
         let content_mask = self.content_mask();
+        let view_id = self.active_view_id();
         let window = &mut *self.window;
         for shadow in shadows {
             let mut shadow_bounds = bounds;
@@ -1037,6 +1032,8 @@ impl<'a> WindowContext<'a> {
             window.next_frame.scene_builder.insert(
                 &window.next_frame.z_index_stack,
                 Shadow {
+                    view_id: view_id.as_u64() as u32,
+                    layer_id: 0,
                     order: 0,
                     bounds: shadow_bounds.scale(scale_factor),
                     content_mask: content_mask.scale(scale_factor),
@@ -1054,11 +1051,14 @@ impl<'a> WindowContext<'a> {
     pub fn paint_quad(&mut self, quad: PaintQuad) {
         let scale_factor = self.scale_factor();
         let content_mask = self.content_mask();
+        let view_id = self.active_view_id();
 
         let window = &mut *self.window;
         window.next_frame.scene_builder.insert(
             &window.next_frame.z_index_stack,
             Quad {
+                view_id: view_id.as_u64() as u32,
+                layer_id: 0,
                 order: 0,
                 bounds: quad.bounds.scale(scale_factor),
                 content_mask: content_mask.scale(scale_factor),
@@ -1074,8 +1074,11 @@ impl<'a> WindowContext<'a> {
     pub fn paint_path(&mut self, mut path: Path<Pixels>, color: impl Into<Hsla>) {
         let scale_factor = self.scale_factor();
         let content_mask = self.content_mask();
+        let view_id = self.active_view_id();
+
         path.content_mask = content_mask;
         path.color = color.into();
+        path.view_id = view_id.as_u64() as u32;
         let window = &mut *self.window;
         window
             .next_frame
@@ -1101,10 +1104,14 @@ impl<'a> WindowContext<'a> {
             size: size(width, height),
         };
         let content_mask = self.content_mask();
+        let view_id = self.active_view_id();
+
         let window = &mut *self.window;
         window.next_frame.scene_builder.insert(
             &window.next_frame.z_index_stack,
             Underline {
+                view_id: view_id.as_u64() as u32,
+                layer_id: 0,
                 order: 0,
                 bounds: bounds.scale(scale_factor),
                 content_mask: content_mask.scale(scale_factor),
@@ -1154,10 +1161,13 @@ impl<'a> WindowContext<'a> {
                 size: tile.bounds.size.map(Into::into),
             };
             let content_mask = self.content_mask().scale(scale_factor);
+            let view_id = self.active_view_id();
             let window = &mut *self.window;
             window.next_frame.scene_builder.insert(
                 &window.next_frame.z_index_stack,
                 MonochromeSprite {
+                    view_id: view_id.as_u64() as u32,
+                    layer_id: 0,
                     order: 0,
                     bounds,
                     content_mask,
@@ -1204,11 +1214,14 @@ impl<'a> WindowContext<'a> {
                 size: tile.bounds.size.map(Into::into),
             };
             let content_mask = self.content_mask().scale(scale_factor);
+            let view_id = self.active_view_id();
             let window = &mut *self.window;
 
             window.next_frame.scene_builder.insert(
                 &window.next_frame.z_index_stack,
                 PolychromeSprite {
+                    view_id: view_id.as_u64() as u32,
+                    layer_id: 0,
                     order: 0,
                     bounds,
                     corner_radii: Default::default(),
@@ -1246,11 +1259,14 @@ impl<'a> WindowContext<'a> {
                     Ok((params.size, Cow::Owned(bytes)))
                 })?;
         let content_mask = self.content_mask().scale(scale_factor);
+        let view_id = self.active_view_id();
 
         let window = &mut *self.window;
         window.next_frame.scene_builder.insert(
             &window.next_frame.z_index_stack,
             MonochromeSprite {
+                view_id: view_id.as_u64() as u32,
+                layer_id: 0,
                 order: 0,
                 bounds,
                 content_mask,
@@ -1282,11 +1298,14 @@ impl<'a> WindowContext<'a> {
             })?;
         let content_mask = self.content_mask().scale(scale_factor);
         let corner_radii = corner_radii.scale(scale_factor);
+        let view_id = self.active_view_id();
 
         let window = &mut *self.window;
         window.next_frame.scene_builder.insert(
             &window.next_frame.z_index_stack,
             PolychromeSprite {
+                view_id: view_id.as_u64() as u32,
+                layer_id: 0,
                 order: 0,
                 bounds,
                 content_mask,
@@ -1303,10 +1322,13 @@ impl<'a> WindowContext<'a> {
         let scale_factor = self.scale_factor();
         let bounds = bounds.scale(scale_factor);
         let content_mask = self.content_mask().scale(scale_factor);
+        let view_id = self.active_view_id();
         let window = &mut *self.window;
         window.next_frame.scene_builder.insert(
             &window.next_frame.z_index_stack,
             Surface {
+                view_id: view_id.as_u64() as u32,
+                layer_id: 0,
                 order: 0,
                 bounds,
                 content_mask,
@@ -1316,13 +1338,23 @@ impl<'a> WindowContext<'a> {
     }
 
     pub(crate) fn reuse_geometry(&mut self) {
+        let view_id = self.active_view_id();
         let window = &mut self.window;
-        let view_id = *window.next_frame.view_stack.last().unwrap();
-        assert!(window.next_frame.reused_views.insert(view_id));
-        window
+        let grafted_view_ids = window
             .next_frame
             .dispatch_tree
-            .graft(view_id, &mut window.rendered_frame.dispatch_tree)
+            .graft(view_id, &mut window.rendered_frame.dispatch_tree);
+        for view_id in grafted_view_ids {
+            assert!(window.next_frame.reused_views.insert(view_id));
+        }
+    }
+
+    fn active_view_id(&self) -> EntityId {
+        self.window
+            .next_frame
+            .dispatch_tree
+            .active_view_id()
+            .expect("a view should always be active")
     }
 
     /// Draw pixels to the display for this window based on the contents of its scene.
@@ -1375,6 +1407,7 @@ impl<'a> WindowContext<'a> {
                     .draw(active_tooltip.cursor_offset, available_space, cx);
             });
         }
+        self.window.dirty_views.clear();
 
         self.window
             .next_frame
@@ -1385,6 +1418,7 @@ impl<'a> WindowContext<'a> {
             );
         self.window.next_frame.focus = self.window.focus;
         self.window.root_view = Some(root_view);
+
         for (type_id, listeners) in &mut self.window.rendered_frame.mouse_listeners {
             let next_listeners = self
                 .window
@@ -1434,7 +1468,6 @@ impl<'a> WindowContext<'a> {
         }
 
         self.window.drawing = false;
-        self.window.dirty_views.clear();
         ELEMENT_ARENA.with_borrow_mut(|element_arena| element_arena.clear());
 
         scene
@@ -2132,9 +2165,13 @@ pub trait BorrowWindow: BorrowMut<Window> + BorrowMut<AppContext> {
     }
 
     fn with_view_id<R>(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R {
-        self.window_mut().next_frame.view_stack.push(view_id);
+        self.window_mut().next_frame.dispatch_tree.push_node(None);
+        self.window_mut()
+            .next_frame
+            .dispatch_tree
+            .associate_view(view_id);
         let result = f(self);
-        self.window_mut().next_frame.view_stack.pop();
+        self.window_mut().next_frame.dispatch_tree.pop_node();
         result
     }
 
@@ -2495,17 +2532,13 @@ impl<'a, V: 'static> ViewContext<'a, V> {
     }
 
     pub fn notify(&mut self) {
-        let mut dirty_view_id = Some(self.view.entity_id());
-        while let Some(view_id) = dirty_view_id {
-            if self.window_cx.window.dirty_views.insert(view_id) {
-                dirty_view_id = self
-                    .window_cx
-                    .window
-                    .rendered_frame
-                    .view_parents
-                    .get(&view_id)
-                    .copied();
-            } else {
+        for view_id in self
+            .window
+            .rendered_frame
+            .dispatch_tree
+            .view_path(self.view.entity_id())
+        {
+            if !self.window.dirty_views.insert(view_id) {
                 break;
             }
         }