diff --git a/crates/gpui/src/key_dispatch.rs b/crates/gpui/src/key_dispatch.rs index 168b1da9fe..a4a048a930 100644 --- a/crates/gpui/src/key_dispatch.rs +++ b/crates/gpui/src/key_dispatch.rs @@ -18,7 +18,6 @@ pub struct DispatchNodeId(usize); pub(crate) struct DispatchTree { node_stack: Vec, pub(crate) context_stack: Vec, - view_stack: Vec, nodes: Vec, focusable_node_ids: FxHashMap, view_node_ids: FxHashMap, @@ -50,7 +49,6 @@ 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(), @@ -63,7 +61,6 @@ impl DispatchTree { pub fn clear(&mut self) { self.node_stack.clear(); self.context_stack.clear(); - self.view_stack.clear(); self.nodes.clear(); self.focusable_node_ids.clear(); self.view_node_ids.clear(); @@ -76,6 +73,15 @@ impl DispatchTree { focus_id: Option, view_id: Option, ) { + // Associate a view id to this only if it is the root node for the view. + let view_id = view_id.and_then(|view_id| { + if self.view_node_ids.contains_key(&view_id) { + None + } else { + Some(view_id) + } + }); + let parent = self.node_stack.last().copied(); let node_id = DispatchNodeId(self.nodes.len()); self.nodes.push(DispatchNode { @@ -96,7 +102,6 @@ impl DispatchTree { } if let Some(view_id) = view_id { - self.view_stack.push(view_id); self.view_node_ids.insert(view_id, node_id); } } @@ -106,21 +111,14 @@ impl DispatchTree { 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) { - self.push_node( - source_node.context.take(), - source_node.focus_id, - source_node.view_id, - ); - let target_node = self.active_node(); - target_node.key_listeners = mem::take(&mut source_node.key_listeners); - target_node.action_listeners = mem::take(&mut source_node.action_listeners); + fn move_node(&mut self, source: &mut DispatchNode) { + self.push_node(source.context.take(), source.focus_id, source.view_id); + let target = self.active_node(); + target.key_listeners = mem::take(&mut source.key_listeners); + target.action_listeners = mem::take(&mut source.action_listeners); } pub fn graft(&mut self, view_id: EntityId, source: &mut Self) -> SmallVec<[EntityId; 8]> { @@ -354,10 +352,6 @@ impl DispatchTree { view_path } - pub fn active_view_id(&self) -> Option { - self.view_stack.last().copied() - } - pub fn node(&self, node_id: DispatchNodeId) -> &DispatchNode { &self.nodes[node_id.0] } diff --git a/crates/gpui/src/view.rs b/crates/gpui/src/view.rs index d049c47258..6ec78e0d2e 100644 --- a/crates/gpui/src/view.rs +++ b/crates/gpui/src/view.rs @@ -89,9 +89,11 @@ impl Element for View { _state: Option, cx: &mut WindowContext, ) -> (LayoutId, Self::State) { - let mut element = self.update(cx, |view, cx| view.render(cx).into_any_element()); - let layout_id = element.request_layout(cx); - (layout_id, Some(element)) + cx.with_view_id(self.entity_id(), |cx| { + let mut element = self.update(cx, |view, cx| view.render(cx).into_any_element()); + let layout_id = element.request_layout(cx); + (layout_id, Some(element)) + }) } fn paint(&mut self, _: Bounds, element: &mut Self::State, cx: &mut WindowContext) { @@ -228,10 +230,12 @@ impl AnyView { available_space: Size, cx: &mut WindowContext, ) { - cx.with_absolute_element_offset(origin, |cx| { - let (layout_id, mut rendered_element) = (self.request_layout)(self, cx); - cx.compute_layout(layout_id, available_space); - cx.with_view_id(self.entity_id(), |cx| rendered_element.paint(cx)); + cx.with_view_id(self.entity_id(), |cx| { + cx.with_absolute_element_offset(origin, |cx| { + let (layout_id, mut rendered_element) = (self.request_layout)(self, cx); + cx.compute_layout(layout_id, available_space); + rendered_element.paint(cx) + }); }) } } @@ -254,21 +258,23 @@ impl Element for AnyView { state: Option, cx: &mut WindowContext, ) -> (LayoutId, Self::State) { - if self.cache { - if let Some(state) = state { - let layout_id = cx.request_layout(&state.root_style, None); - return (layout_id, state); + cx.with_view_id(self.entity_id(), |cx| { + if self.cache { + if let Some(state) = state { + let layout_id = cx.request_layout(&state.root_style, None); + return (layout_id, state); + } } - } - let (layout_id, element) = (self.request_layout)(self, cx); - let root_style = cx.layout_style(layout_id).unwrap().clone(); - let state = AnyViewState { - root_style, - cache_key: None, - element: Some(element), - }; - (layout_id, state) + let (layout_id, element) = (self.request_layout)(self, cx); + let root_style = cx.layout_style(layout_id).unwrap().clone(); + let state = AnyViewState { + root_style, + cache_key: None, + element: Some(element), + }; + (layout_id, state) + }) } fn paint(&mut self, bounds: Bounds, state: &mut Self::State, cx: &mut WindowContext) { diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 45dc197e18..376f4c2466 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -280,6 +280,7 @@ pub struct Window { pub(crate) struct ElementStateBox { inner: Box, + parent_view_id: EntityId, #[cfg(debug_assertions)] type_name: &'static str, } @@ -290,11 +291,12 @@ pub(crate) struct Frame { mouse_listeners: FxHashMap>, pub(crate) dispatch_tree: DispatchTree, pub(crate) scene: Scene, - pub(crate) depth_map: Vec<(StackingOrder, Bounds)>, + pub(crate) depth_map: Vec<(StackingOrder, EntityId, Bounds)>, pub(crate) z_index_stack: StackingOrder, pub(crate) next_stacking_order_id: u32, content_mask_stack: Vec>, element_offset_stack: Vec>, + pub(crate) view_stack: Vec, pub(crate) reused_views: FxHashSet, } @@ -311,6 +313,7 @@ impl Frame { depth_map: Default::default(), content_mask_stack: Vec::new(), element_offset_stack: Vec::new(), + view_stack: Vec::new(), reused_views: FxHashSet::default(), } } @@ -323,6 +326,7 @@ impl Frame { self.next_stacking_order_id = 0; self.reused_views.clear(); self.scene.clear(); + debug_assert_eq!(self.view_stack.len(), 0); } fn focus_path(&self) -> SmallVec<[FocusId; 8]> { @@ -880,7 +884,7 @@ impl<'a> WindowContext<'a> { &mut self, mut handler: impl FnMut(&Event, DispatchPhase, &mut WindowContext) + 'static, ) { - let view_id = self.active_view_id(); + let view_id = self.parent_view_id().unwrap(); let order = self.window.next_frame.z_index_stack.clone(); self.window .next_frame @@ -967,17 +971,18 @@ impl<'a> WindowContext<'a> { /// Called during painting to track which z-index is on top at each pixel position pub fn add_opaque_layer(&mut self, bounds: Bounds) { let stacking_order = self.window.next_frame.z_index_stack.clone(); - let depth_map = &mut self.window.next_frame.depth_map; - match depth_map.binary_search_by(|(level, _)| stacking_order.cmp(level)) { - Ok(i) | Err(i) => depth_map.insert(i, (stacking_order, bounds)), - } + let view_id = self.parent_view_id().unwrap(); + self.window + .next_frame + .depth_map + .push((stacking_order, view_id, bounds)); } /// Returns true if there is no opaque layer containing the given point /// on top of the given level. Layers whose level is an extension of the /// level are not considered to be on top of the level. pub fn was_top_layer(&self, point: &Point, level: &StackingOrder) -> bool { - for (opaque_level, bounds) in self.window.rendered_frame.depth_map.iter() { + for (opaque_level, _, bounds) in self.window.rendered_frame.depth_map.iter() { if level >= opaque_level { break; } @@ -994,7 +999,7 @@ impl<'a> WindowContext<'a> { point: &Point, level: &StackingOrder, ) -> bool { - for (opaque_level, bounds) in self.window.rendered_frame.depth_map.iter() { + for (opaque_level, _, bounds) in self.window.rendered_frame.depth_map.iter() { if level >= opaque_level { break; } @@ -1023,7 +1028,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 view_id = self.parent_view_id().unwrap(); let window = &mut *self.window; for shadow in shadows { let mut shadow_bounds = bounds; @@ -1051,7 +1056,7 @@ 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 view_id = self.parent_view_id().unwrap(); let window = &mut *self.window; window.next_frame.scene.insert( @@ -1074,7 +1079,7 @@ impl<'a> WindowContext<'a> { pub fn paint_path(&mut self, mut path: Path, color: impl Into) { let scale_factor = self.scale_factor(); let content_mask = self.content_mask(); - let view_id = self.active_view_id(); + let view_id = self.parent_view_id().unwrap(); path.content_mask = content_mask; path.color = color.into(); @@ -1104,7 +1109,7 @@ impl<'a> WindowContext<'a> { size: size(width, height), }; let content_mask = self.content_mask(); - let view_id = self.active_view_id(); + let view_id = self.parent_view_id().unwrap(); let window = &mut *self.window; window.next_frame.scene.insert( @@ -1161,7 +1166,7 @@ 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 view_id = self.parent_view_id().unwrap(); let window = &mut *self.window; window.next_frame.scene.insert( &window.next_frame.z_index_stack, @@ -1214,7 +1219,7 @@ 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 view_id = self.parent_view_id().unwrap(); let window = &mut *self.window; window.next_frame.scene.insert( @@ -1259,7 +1264,7 @@ 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 view_id = self.parent_view_id().unwrap(); let window = &mut *self.window; window.next_frame.scene.insert( @@ -1298,7 +1303,7 @@ 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 view_id = self.parent_view_id().unwrap(); let window = &mut *self.window; window.next_frame.scene.insert( @@ -1322,7 +1327,7 @@ 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 view_id = self.parent_view_id().unwrap(); let window = &mut *self.window; window.next_frame.scene.insert( &window.next_frame.z_index_stack, @@ -1338,8 +1343,7 @@ impl<'a> WindowContext<'a> { } pub(crate) fn reuse_geometry(&mut self) { - println!("reusing geometry"); - let view_id = self.active_view_id(); + let view_id = self.parent_view_id().unwrap(); let window = &mut self.window; let grafted_view_ids = window .next_frame @@ -1350,17 +1354,8 @@ impl<'a> WindowContext<'a> { } } - 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. pub(crate) fn draw(&mut self) { - println!("====================="); self.window.dirty = false; self.window.drawing = true; @@ -1409,11 +1404,6 @@ impl<'a> WindowContext<'a> { }); } self.window.dirty_views.clear(); - self.window.next_frame.scene.insert_views_from_scene( - &self.window.next_frame.reused_views, - &mut self.window.rendered_frame.scene, - ); - self.window.next_frame.scene.finish(); self.window .next_frame @@ -1425,6 +1415,7 @@ impl<'a> WindowContext<'a> { self.window.next_frame.focus = self.window.focus; self.window.root_view = Some(root_view); + // Reuse mouse listeners that didn't change since the last frame. for (type_id, listeners) in &mut self.window.rendered_frame.mouse_listeners { let next_listeners = self .window @@ -1439,6 +1430,43 @@ impl<'a> WindowContext<'a> { } } + // Reuse entries in the depth map that didn't change since the last frame. + for (order, view_id, bounds) in self.window.rendered_frame.depth_map.drain(..) { + if self.window.next_frame.reused_views.contains(&view_id) { + self.window + .next_frame + .depth_map + .push((order, view_id, bounds)); + } + } + self.window + .next_frame + .depth_map + .sort_by(|a, b| a.0.cmp(&b.0)); + + // Retain element states for views that didn't change since the last frame. + for (element_id, state) in self.window.rendered_frame.element_states.drain() { + if self + .window + .next_frame + .reused_views + .contains(&state.parent_view_id) + { + self.window + .next_frame + .element_states + .entry(element_id) + .or_insert(state); + } + } + + // Reuse geometry that didn't change since the last frame. + self.window.next_frame.scene.insert_views_from_scene( + &self.window.next_frame.reused_views, + &mut self.window.rendered_frame.scene, + ); + self.window.next_frame.scene.finish(); + let previous_focus_path = self.window.rendered_frame.focus_path(); mem::swap(&mut self.window.rendered_frame, &mut self.window.next_frame); let current_focus_path = self.window.rendered_frame.focus_path(); @@ -1871,12 +1899,13 @@ impl<'a> WindowContext<'a> { focus_handle: Option, f: impl FnOnce(Option, &mut Self) -> R, ) -> R { + let parent_view_id = self.parent_view_id(); let window = &mut self.window; let focus_id = focus_handle.as_ref().map(|handle| handle.id); window .next_frame .dispatch_tree - .push_node(context.clone(), focus_id, None); + .push_node(context.clone(), focus_id, parent_view_id); let result = f(focus_handle, self); @@ -1885,6 +1914,114 @@ impl<'a> WindowContext<'a> { result } + pub(crate) fn with_view_id( + &mut self, + view_id: EntityId, + f: impl FnOnce(&mut Self) -> R, + ) -> R { + self.window.next_frame.view_stack.push(view_id); + let result = f(self); + self.window.next_frame.view_stack.pop(); + result + } + + /// Update or initialize state for an element with the given id that lives across multiple + /// frames. If an element with this id existed in the rendered frame, its state will be passed + /// to the given closure. The state returned by the closure will be stored so it can be referenced + /// when drawing the next frame. + pub(crate) fn with_element_state( + &mut self, + id: ElementId, + f: impl FnOnce(Option, &mut Self) -> (R, S), + ) -> R + where + S: 'static, + { + self.with_element_id(Some(id), |cx| { + let global_id = cx.window().element_id_stack.clone(); + + if let Some(any) = cx + .window_mut() + .next_frame + .element_states + .remove(&global_id) + .or_else(|| { + cx.window_mut() + .rendered_frame + .element_states + .remove(&global_id) + }) + { + let ElementStateBox { + inner, + parent_view_id, + #[cfg(debug_assertions)] + type_name + } = any; + // Using the extra inner option to avoid needing to reallocate a new box. + let mut state_box = inner + .downcast::>() + .map_err(|_| { + #[cfg(debug_assertions)] + { + anyhow!( + "invalid element state type for id, requested_type {:?}, actual type: {:?}", + std::any::type_name::(), + type_name + ) + } + + #[cfg(not(debug_assertions))] + { + anyhow!( + "invalid element state type for id, requested_type {:?}", + std::any::type_name::(), + ) + } + }) + .unwrap(); + + // Actual: Option <- View + // Requested: () <- AnyElemet + let state = state_box + .take() + .expect("element state is already on the stack"); + let (result, state) = f(Some(state), cx); + state_box.replace(state); + cx.window_mut() + .next_frame + .element_states + .insert(global_id, ElementStateBox { + inner: state_box, + parent_view_id, + #[cfg(debug_assertions)] + type_name + }); + result + } else { + let (result, state) = f(None, cx); + let parent_view_id = cx.parent_view_id().unwrap(); + cx.window_mut() + .next_frame + .element_states + .insert(global_id, + ElementStateBox { + inner: Box::new(Some(state)), + parent_view_id, + #[cfg(debug_assertions)] + type_name: std::any::type_name::() + } + + ); + result + } + }) + } + + fn parent_view_id(&self) -> Option { + self.window.next_frame.view_stack.last().copied() + } + /// Set an input handler, such as [`ElementInputHandler`][element_input_handler], which interfaces with the /// platform to receive textual input with proper integration with concerns such /// as IME interactions. @@ -2169,16 +2306,6 @@ pub trait BorrowWindow: BorrowMut + BorrowMut { result } - fn with_view_id(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R { - self.window_mut() - .next_frame - .dispatch_tree - .push_node(None, None, Some(view_id)); - let result = f(self); - self.window_mut().next_frame.dispatch_tree.pop_node(); - result - } - /// Update the global element offset relative to the current offset. This is used to implement /// scrolling. fn with_element_offset( @@ -2220,98 +2347,6 @@ pub trait BorrowWindow: BorrowMut + BorrowMut { .unwrap_or_default() } - /// Update or initialize state for an element with the given id that lives across multiple - /// frames. If an element with this id existed in the rendered frame, its state will be passed - /// to the given closure. The state returned by the closure will be stored so it can be referenced - /// when drawing the next frame. - fn with_element_state( - &mut self, - id: ElementId, - f: impl FnOnce(Option, &mut Self) -> (R, S), - ) -> R - where - S: 'static, - { - self.with_element_id(Some(id), |cx| { - let global_id = cx.window().element_id_stack.clone(); - - if let Some(any) = cx - .window_mut() - .next_frame - .element_states - .remove(&global_id) - .or_else(|| { - cx.window_mut() - .rendered_frame - .element_states - .remove(&global_id) - }) - { - let ElementStateBox { - inner, - - #[cfg(debug_assertions)] - type_name - } = any; - // Using the extra inner option to avoid needing to reallocate a new box. - let mut state_box = inner - .downcast::>() - .map_err(|_| { - #[cfg(debug_assertions)] - { - anyhow!( - "invalid element state type for id, requested_type {:?}, actual type: {:?}", - std::any::type_name::(), - type_name - ) - } - - #[cfg(not(debug_assertions))] - { - anyhow!( - "invalid element state type for id, requested_type {:?}", - std::any::type_name::(), - ) - } - }) - .unwrap(); - - // Actual: Option <- View - // Requested: () <- AnyElemet - let state = state_box - .take() - .expect("element state is already on the stack"); - let (result, state) = f(Some(state), cx); - state_box.replace(state); - cx.window_mut() - .next_frame - .element_states - .insert(global_id, ElementStateBox { - inner: state_box, - - #[cfg(debug_assertions)] - type_name - }); - result - } else { - let (result, state) = f(None, cx); - cx.window_mut() - .next_frame - .element_states - .insert(global_id, - ElementStateBox { - inner: Box::new(Some(state)), - - #[cfg(debug_assertions)] - type_name: std::any::type_name::() - } - - ); - result - } - }) - } - /// Obtain the current content mask. fn content_mask(&self) -> ContentMask { self.window()