From 5904bcf1c20638d63b244a1b2b038ec9a664ba1c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 8 Jan 2024 13:51:38 +0100 Subject: [PATCH 01/22] Use taffy to retrieve the parent for a given layout node --- Cargo.lock | 7 +++--- crates/gpui/Cargo.toml | 2 +- crates/gpui/src/taffy.rs | 46 ++++++++++++++++------------------------ 3 files changed, 23 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 54e2f483d8a655f3d5e5c6e200b918275f968a34..ca55567cbb2a78661cb3c91b3e31c89b281bb9b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3091,9 +3091,9 @@ dependencies = [ [[package]] name = "grid" -version = "0.11.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df00eed8d1f0db937f6be10e46e8072b0671accb504cf0f959c5c52c679f5b9" +checksum = "d196ffc1627db18a531359249b2bf8416178d84b729f3cebeb278f285fb9b58c" [[package]] name = "h2" @@ -7665,11 +7665,12 @@ dependencies = [ [[package]] name = "taffy" version = "0.3.11" -source = "git+https://github.com/DioxusLabs/taffy?rev=1876f72bee5e376023eaa518aa7b8a34c769bd1b#1876f72bee5e376023eaa518aa7b8a34c769bd1b" +source = "git+https://github.com/zed-industries/taffy?rev=5e6c2d23e70e9f2156911d11050cb686362ba277#5e6c2d23e70e9f2156911d11050cb686362ba277" dependencies = [ "arrayvec 0.7.4", "grid", "num-traits", + "serde", "slotmap", ] diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 6ea3524fcc4eb2a60ae2411f7eed6a2ef05a17a8..118753aafea5981e0dd2119f9b4203c70b85695d 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -46,7 +46,7 @@ serde_derive.workspace = true serde_json.workspace = true smallvec.workspace = true smol.workspace = true -taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "1876f72bee5e376023eaa518aa7b8a34c769bd1b" } +taffy = { git = "https://github.com/zed-industries/taffy", rev = "5e6c2d23e70e9f2156911d11050cb686362ba277" } thiserror.workspace = true time.workspace = true tiny-skia = "0.5" diff --git a/crates/gpui/src/taffy.rs b/crates/gpui/src/taffy.rs index 0ebd394217ae06c6a2256281f389ab506bfca934..a77127e80176f6f74ca5f789a2570d0a630f9a33 100644 --- a/crates/gpui/src/taffy.rs +++ b/crates/gpui/src/taffy.rs @@ -6,15 +6,12 @@ use collections::{FxHashMap, FxHashSet}; use smallvec::SmallVec; use std::fmt::Debug; use taffy::{ - geometry::{Point as TaffyPoint, Rect as TaffyRect, Size as TaffySize}, - style::AvailableSpace as TaffyAvailableSpace, - tree::NodeId, - Taffy, + AvailableSpace as TaffyAvailableSpace, NodeId, Point as TaffyPoint, Rect as TaffyRect, + Size as TaffySize, TaffyTree, TraversePartialTree, }; pub struct TaffyLayoutEngine { - taffy: Taffy, - children_to_parents: FxHashMap, + tree: TaffyTree, absolute_layout_bounds: FxHashMap>, computed_layouts: FxHashSet, nodes_to_measure: FxHashMap< @@ -34,8 +31,7 @@ static EXPECT_MESSAGE: &str = "we should avoid taffy layout errors by constructi impl TaffyLayoutEngine { pub fn new() -> Self { TaffyLayoutEngine { - taffy: Taffy::new(), - children_to_parents: FxHashMap::default(), + tree: TaffyTree::new(), absolute_layout_bounds: FxHashMap::default(), computed_layouts: FxHashSet::default(), nodes_to_measure: FxHashMap::default(), @@ -43,8 +39,7 @@ impl TaffyLayoutEngine { } pub fn clear(&mut self) { - self.taffy.clear(); - self.children_to_parents.clear(); + self.tree.clear(); self.absolute_layout_bounds.clear(); self.computed_layouts.clear(); self.nodes_to_measure.clear(); @@ -58,18 +53,13 @@ impl TaffyLayoutEngine { ) -> LayoutId { let style = style.to_taffy(rem_size); if children.is_empty() { - self.taffy.new_leaf(style).expect(EXPECT_MESSAGE).into() + self.tree.new_leaf(style).expect(EXPECT_MESSAGE).into() } else { - let parent_id = self - .taffy + self.tree // This is safe because LayoutId is repr(transparent) to taffy::tree::NodeId. .new_with_children(style, unsafe { std::mem::transmute(children) }) .expect(EXPECT_MESSAGE) - .into(); - for child_id in children { - self.children_to_parents.insert(*child_id, parent_id); - } - parent_id + .into() } } @@ -83,7 +73,7 @@ impl TaffyLayoutEngine { let style = style.to_taffy(rem_size); let layout_id = self - .taffy + .tree .new_leaf_with_context(style, ()) .expect(EXPECT_MESSAGE) .into(); @@ -96,7 +86,7 @@ impl TaffyLayoutEngine { fn count_all_children(&self, parent: LayoutId) -> anyhow::Result { let mut count = 0; - for child in self.taffy.children(parent.0)? { + for child in self.tree.children(parent.0)? { // Count this child. count += 1; @@ -112,12 +102,12 @@ impl TaffyLayoutEngine { fn max_depth(&self, depth: u32, parent: LayoutId) -> anyhow::Result { println!( "{parent:?} at depth {depth} has {} children", - self.taffy.child_count(parent.0)? + self.tree.child_count(parent.0) ); let mut max_child_depth = 0; - for child in self.taffy.children(parent.0)? { + for child in self.tree.children(parent.0)? { max_child_depth = std::cmp::max(max_child_depth, self.max_depth(0, LayoutId(child))?); } @@ -129,7 +119,7 @@ impl TaffyLayoutEngine { fn get_edges(&self, parent: LayoutId) -> anyhow::Result> { let mut edges = Vec::new(); - for child in self.taffy.children(parent.0)? { + for child in self.tree.children(parent.0)? { edges.push((parent, LayoutId(child))); edges.extend(self.get_edges(LayoutId(child))?); @@ -162,7 +152,7 @@ impl TaffyLayoutEngine { while let Some(id) = stack.pop() { self.absolute_layout_bounds.remove(&id); stack.extend( - self.taffy + self.tree .children(id.into()) .expect(EXPECT_MESSAGE) .into_iter() @@ -172,7 +162,7 @@ impl TaffyLayoutEngine { } // let started_at = std::time::Instant::now(); - self.taffy + self.tree .compute_layout_with_measure( id.into(), available_space.into(), @@ -199,14 +189,14 @@ impl TaffyLayoutEngine { return layout; } - let layout = self.taffy.layout(id.into()).expect(EXPECT_MESSAGE); + let layout = self.tree.layout(id.into()).expect(EXPECT_MESSAGE); let mut bounds = Bounds { origin: layout.location.into(), size: layout.size.into(), }; - if let Some(parent_id) = self.children_to_parents.get(&id).copied() { - let parent_bounds = self.layout_bounds(parent_id); + if let Some(parent_id) = self.tree.parent(id.0) { + let parent_bounds = self.layout_bounds(parent_id.into()); bounds.origin += parent_bounds.origin; } self.absolute_layout_bounds.insert(id, bounds); From 84c36066bcc20e3ef854d1527f01595e1e93fdf7 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 8 Jan 2024 19:07:20 +0100 Subject: [PATCH 02/22] Start on caching views Co-Authored-By: Nathan Sobo --- crates/gpui/src/geometry.rs | 6 +- crates/gpui/src/style.rs | 2 +- crates/gpui/src/taffy.rs | 28 +++++-- crates/gpui/src/view.rs | 122 +++++++++++++++++++---------- crates/gpui/src/window.rs | 9 +++ crates/workspace/src/dock.rs | 2 +- crates/workspace/src/pane_group.rs | 6 +- 7 files changed, 117 insertions(+), 58 deletions(-) diff --git a/crates/gpui/src/geometry.rs b/crates/gpui/src/geometry.rs index a50de8c344247c705d24c8d9a0e94c7c799278bb..89e47994a34b76d4310e3d8afe0c15be411608be 100644 --- a/crates/gpui/src/geometry.rs +++ b/crates/gpui/src/geometry.rs @@ -2272,7 +2272,7 @@ impl From for GlobalPixels { /// For example, if the root element's font-size is `16px`, then `1rem` equals `16px`. A length of `2rems` would then be `32px`. /// /// [set_rem_size]: crate::WindowContext::set_rem_size -#[derive(Clone, Copy, Default, Add, Sub, Mul, Div, Neg)] +#[derive(Clone, Copy, Default, Add, Sub, Mul, Div, Neg, PartialEq)] pub struct Rems(pub f32); impl Mul for Rems { @@ -2295,7 +2295,7 @@ impl Debug for Rems { /// affected by the current font size, or a number of rems, which is relative to the font size of /// the root element. It is used for specifying dimensions that are either independent of or /// related to the typographic scale. -#[derive(Clone, Copy, Debug, Neg)] +#[derive(Clone, Copy, Debug, Neg, PartialEq)] pub enum AbsoluteLength { /// A length in pixels. Pixels(Pixels), @@ -2366,7 +2366,7 @@ impl Default for AbsoluteLength { /// This enum represents lengths that have a specific value, as opposed to lengths that are automatically /// determined by the context. It includes absolute lengths in pixels or rems, and relative lengths as a /// fraction of the parent's size. -#[derive(Clone, Copy, Neg)] +#[derive(Clone, Copy, Neg, PartialEq)] pub enum DefiniteLength { /// An absolute length specified in pixels or rems. Absolute(AbsoluteLength), diff --git a/crates/gpui/src/style.rs b/crates/gpui/src/style.rs index 244ccebf2498fb9ff275d0818215a9ba658ffc02..67b9b855d26ac159566daf748c4a8136b83f5cc0 100644 --- a/crates/gpui/src/style.rs +++ b/crates/gpui/src/style.rs @@ -146,7 +146,7 @@ pub enum WhiteSpace { Nowrap, } -#[derive(Refineable, Clone, Debug)] +#[derive(Refineable, Clone, Debug, PartialEq)] #[refineable(Debug)] pub struct TextStyle { pub color: Hsla, diff --git a/crates/gpui/src/taffy.rs b/crates/gpui/src/taffy.rs index a77127e80176f6f74ca5f789a2570d0a630f9a33..430d928cbf5bdc55f27928a9d5c65bb6aa2645d7 100644 --- a/crates/gpui/src/taffy.rs +++ b/crates/gpui/src/taffy.rs @@ -12,6 +12,7 @@ use taffy::{ pub struct TaffyLayoutEngine { tree: TaffyTree, + styles: FxHashMap, absolute_layout_bounds: FxHashMap>, computed_layouts: FxHashSet, nodes_to_measure: FxHashMap< @@ -32,6 +33,7 @@ impl TaffyLayoutEngine { pub fn new() -> Self { TaffyLayoutEngine { tree: TaffyTree::new(), + styles: FxHashMap::default(), absolute_layout_bounds: FxHashMap::default(), computed_layouts: FxHashSet::default(), nodes_to_measure: FxHashMap::default(), @@ -43,6 +45,11 @@ impl TaffyLayoutEngine { self.absolute_layout_bounds.clear(); self.computed_layouts.clear(); self.nodes_to_measure.clear(); + self.styles.clear(); + } + + pub fn requested_style(&self, layout_id: LayoutId) -> Option<&Style> { + self.styles.get(&layout_id) } pub fn request_layout( @@ -51,16 +58,21 @@ impl TaffyLayoutEngine { rem_size: Pixels, children: &[LayoutId], ) -> LayoutId { - let style = style.to_taffy(rem_size); - if children.is_empty() { - self.tree.new_leaf(style).expect(EXPECT_MESSAGE).into() + let taffy_style = style.to_taffy(rem_size); + let layout_id = if children.is_empty() { + self.tree + .new_leaf(taffy_style) + .expect(EXPECT_MESSAGE) + .into() } else { self.tree // This is safe because LayoutId is repr(transparent) to taffy::tree::NodeId. - .new_with_children(style, unsafe { std::mem::transmute(children) }) + .new_with_children(taffy_style, unsafe { std::mem::transmute(children) }) .expect(EXPECT_MESSAGE) .into() - } + }; + self.styles.insert(layout_id, style.clone()); + layout_id } pub fn request_measured_layout( @@ -70,14 +82,16 @@ impl TaffyLayoutEngine { measure: impl FnMut(Size>, Size, &mut WindowContext) -> Size + 'static, ) -> LayoutId { - let style = style.to_taffy(rem_size); + let style = style.clone(); + let taffy_style = style.to_taffy(rem_size); let layout_id = self .tree - .new_leaf_with_context(style, ()) + .new_leaf_with_context(taffy_style, ()) .expect(EXPECT_MESSAGE) .into(); self.nodes_to_measure.insert(layout_id, Box::new(measure)); + self.styles.insert(layout_id, style.clone()); layout_id } diff --git a/crates/gpui/src/view.rs b/crates/gpui/src/view.rs index 4472da02e71fda1bb17d4353056b67ad58639813..7d9b8092ee10531f6c80d1d874016bb8ebd00108 100644 --- a/crates/gpui/src/view.rs +++ b/crates/gpui/src/view.rs @@ -1,8 +1,8 @@ use crate::{ seal::Sealed, AnyElement, AnyModel, AnyWeakModel, AppContext, AvailableSpace, BorrowWindow, - Bounds, Element, ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView, IntoElement, - LayoutId, Model, Pixels, Point, Render, Size, ViewContext, VisualContext, WeakModel, - WindowContext, + Bounds, ContentMask, Element, ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView, + IntoElement, LayoutId, Model, Pixels, Point, Render, Size, StackingOrder, Style, TextStyle, + ViewContext, VisualContext, WeakModel, WindowContext, }; use anyhow::{Context, Result}; use std::{ @@ -17,6 +17,19 @@ pub struct View { impl Sealed for View {} +pub struct AnyViewState { + root_style: Style, + cache_key: Option, + element: Option, +} + +struct ViewCacheKey { + bounds: Bounds, + stacking_order: StackingOrder, + content_mask: ContentMask, + text_style: TextStyle, +} + impl Entity for View { type Weak = WeakView; @@ -60,16 +73,6 @@ impl View { self.model.read(cx) } - // pub fn render_with(&self, component: E) -> RenderViewWith - // where - // E: 'static + Element, - // { - // RenderViewWith { - // view: self.clone(), - // element: Some(component), - // } - // } - pub fn focus_handle(&self, cx: &AppContext) -> FocusHandle where V: FocusableView, @@ -183,16 +186,20 @@ impl Eq for WeakView {} #[derive(Clone, Debug)] pub struct AnyView { model: AnyModel, - layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, AnyElement), - paint: fn(&AnyView, &mut AnyElement, &mut WindowContext), + request_layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, AnyElement), + cache: bool, } impl AnyView { + pub fn cached(mut self) -> Self { + self.cache = true; + self + } + pub fn downgrade(&self) -> AnyWeakView { AnyWeakView { model: self.model.downgrade(), - layout: self.layout, - paint: self.paint, + layout: self.request_layout, } } @@ -201,8 +208,8 @@ impl AnyView { Ok(model) => Ok(View { model }), Err(model) => Err(Self { model, - layout: self.layout, - paint: self.paint, + request_layout: self.request_layout, + cache: self.cache, }), } } @@ -222,9 +229,9 @@ impl AnyView { cx: &mut WindowContext, ) { cx.with_absolute_element_offset(origin, |cx| { - let (layout_id, mut rendered_element) = (self.layout)(self, cx); + let (layout_id, mut rendered_element) = (self.request_layout)(self, cx); cx.compute_layout(layout_id, available_space); - (self.paint)(self, &mut rendered_element, cx); + rendered_element.paint(cx); }) } } @@ -233,30 +240,65 @@ impl From> for AnyView { fn from(value: View) -> Self { AnyView { model: value.model.into_any(), - layout: any_view::layout::, - paint: any_view::paint, + request_layout: any_view::request_layout::, + cache: false, } } } impl Element for AnyView { - type State = Option; + type State = AnyViewState; fn request_layout( &mut self, - _state: Option, + state: Option, cx: &mut WindowContext, ) -> (LayoutId, Self::State) { - let (layout_id, state) = (self.layout)(self, cx); - (layout_id, Some(state)) + 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) } - fn paint(&mut self, _: Bounds, state: &mut Self::State, cx: &mut WindowContext) { - debug_assert!( - state.is_some(), - "state is None. Did you include an AnyView twice in the tree?" - ); - (self.paint)(self, state.as_mut().unwrap(), cx) + fn paint(&mut self, bounds: Bounds, state: &mut Self::State, cx: &mut WindowContext) { + if !self.cache { + state.element.take().unwrap().paint(cx); + return; + } + + if let Some(cache_key) = state.cache_key.as_mut() { + if cache_key.bounds == bounds + && cache_key.content_mask == cx.content_mask() + && cache_key.stacking_order == *cx.stacking_order() + && cache_key.text_style == cx.text_style() + { + println!("could reuse geometry for view {}", self.entity_id()); + } + } + + let mut element = state + .element + .take() + .unwrap_or_else(|| (self.request_layout)(self, cx).1); + element.draw(bounds.origin, bounds.size.into(), cx); + + state.cache_key = Some(ViewCacheKey { + bounds, + stacking_order: cx.stacking_order().clone(), + content_mask: cx.content_mask(), + text_style: cx.text_style(), + }); } } @@ -287,7 +329,6 @@ impl IntoElement for AnyView { pub struct AnyWeakView { model: AnyWeakModel, layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, AnyElement), - paint: fn(&AnyView, &mut AnyElement, &mut WindowContext), } impl AnyWeakView { @@ -295,8 +336,8 @@ impl AnyWeakView { let model = self.model.upgrade()?; Some(AnyView { model, - layout: self.layout, - paint: self.paint, + request_layout: self.layout, + cache: false, }) } } @@ -305,8 +346,7 @@ impl From> for AnyWeakView { fn from(view: WeakView) -> Self { Self { model: view.model.into(), - layout: any_view::layout::, - paint: any_view::paint, + layout: any_view::request_layout::, } } } @@ -328,7 +368,7 @@ impl std::fmt::Debug for AnyWeakView { mod any_view { use crate::{AnyElement, AnyView, IntoElement, LayoutId, Render, WindowContext}; - pub(crate) fn layout( + pub(crate) fn request_layout( view: &AnyView, cx: &mut WindowContext, ) -> (LayoutId, AnyElement) { @@ -337,8 +377,4 @@ mod any_view { let layout_id = element.request_layout(cx); (layout_id, element) } - - pub(crate) fn paint(_view: &AnyView, element: &mut AnyElement, cx: &mut WindowContext) { - element.paint(cx); - } } diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 7e4c5f93f95e6ea770d404a63a3e6795d9a4be7d..edd98e8385a324ca91b2a8d04b8ab7107096ecf8 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -754,6 +754,14 @@ impl<'a> WindowContext<'a> { .request_measured_layout(style, rem_size, measure) } + pub fn layout_style(&self, layout_id: LayoutId) -> Option<&Style> { + self.window + .layout_engine + .as_ref() + .unwrap() + .requested_style(layout_id) + } + pub fn compute_layout(&mut self, layout_id: LayoutId, available_space: Size) { let mut layout_engine = self.window.layout_engine.take().unwrap(); layout_engine.compute_layout(layout_id, available_space, self); @@ -1313,6 +1321,7 @@ impl<'a> WindowContext<'a> { /// Draw pixels to the display for this window based on the contents of its scene. pub(crate) fn draw(&mut self) -> Scene { + println!("====================="); self.window.dirty = false; self.window.drawing = true; diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index c13a00b11c897b46ef5e2ac69ae10848c573ebf2..8a16ce5ab6ce7ebf026cbef03f6dbaf883f0f18d 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -601,7 +601,7 @@ impl Render for Dock { Axis::Horizontal => this.min_w(size).h_full(), Axis::Vertical => this.min_h(size).w_full(), }) - .child(entry.panel.to_any()), + .child(entry.panel.to_any().cached()), ) .child(handle) } else { diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index a7368f61360ce6639dffa26ffd31f2114cd2216b..236daf60f8a747f4d43f4bc0a73109cd16e2ad3e 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -3,8 +3,8 @@ use anyhow::{anyhow, Result}; use call::{ActiveCall, ParticipantLocation}; use collections::HashMap; use gpui::{ - point, size, AnyWeakView, Axis, Bounds, Entity as _, IntoElement, Model, Pixels, Point, View, - ViewContext, + point, size, AnyView, AnyWeakView, Axis, Bounds, Entity as _, IntoElement, Model, Pixels, + Point, View, ViewContext, }; use parking_lot::Mutex; use project::Project; @@ -244,7 +244,7 @@ impl Member { .relative() .flex_1() .size_full() - .child(pane.clone()) + .child(AnyView::from(pane.clone()).cached()) .when_some(leader_border, |this, color| { this.child( div() From c9193b586b7a53ddd29308bcc19f660b7b8e05a9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 8 Jan 2024 19:31:50 +0100 Subject: [PATCH 03/22] WIP --- crates/gpui/src/view.rs | 53 +++++++++++++++++++++------------------ crates/gpui/src/window.rs | 33 +++++++++++++++++++++++- 2 files changed, 60 insertions(+), 26 deletions(-) diff --git a/crates/gpui/src/view.rs b/crates/gpui/src/view.rs index 7d9b8092ee10531f6c80d1d874016bb8ebd00108..9d504627ab185212df7df14399dfc54bcf1d6b46 100644 --- a/crates/gpui/src/view.rs +++ b/crates/gpui/src/view.rs @@ -95,7 +95,7 @@ impl Element for View { } fn paint(&mut self, _: Bounds, element: &mut Self::State, cx: &mut WindowContext) { - element.take().unwrap().paint(cx); + cx.with_view_id(self.entity_id(), |cx| element.take().unwrap().paint(cx)); } } @@ -272,33 +272,36 @@ impl Element for AnyView { } fn paint(&mut self, bounds: Bounds, state: &mut Self::State, cx: &mut WindowContext) { - if !self.cache { - state.element.take().unwrap().paint(cx); - return; - } + cx.with_view_id(self.entity_id(), |cx| { + if !self.cache { + state.element.take().unwrap().paint(cx); + return; + } - if let Some(cache_key) = state.cache_key.as_mut() { - if cache_key.bounds == bounds - && cache_key.content_mask == cx.content_mask() - && cache_key.stacking_order == *cx.stacking_order() - && cache_key.text_style == cx.text_style() - { - println!("could reuse geometry for view {}", self.entity_id()); + if let Some(cache_key) = state.cache_key.as_mut() { + if cache_key.bounds == bounds + && cache_key.content_mask == cx.content_mask() + && cache_key.stacking_order == *cx.stacking_order() + && cache_key.text_style == cx.text_style() + && !cx.window.dirty_views.contains(&self.entity_id()) + { + println!("could reuse geometry for view {}", self.entity_id()); + } } - } - let mut element = state - .element - .take() - .unwrap_or_else(|| (self.request_layout)(self, cx).1); - element.draw(bounds.origin, bounds.size.into(), cx); - - state.cache_key = Some(ViewCacheKey { - bounds, - stacking_order: cx.stacking_order().clone(), - content_mask: cx.content_mask(), - text_style: cx.text_style(), - }); + let mut element = state + .element + .take() + .unwrap_or_else(|| (self.request_layout)(self, cx).1); + element.draw(bounds.origin, bounds.size.into(), cx); + + state.cache_key = Some(ViewCacheKey { + bounds, + stacking_order: cx.stacking_order().clone(), + content_mask: cx.content_mask(), + text_style: cx.text_style(), + }); + }) } } diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index edd98e8385a324ca91b2a8d04b8ab7107096ecf8..98c81d3883122066edef4254d90b8adb6ba0ad3c 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -12,7 +12,7 @@ use crate::{ VisualContext, WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::{anyhow, Context as _, Result}; -use collections::FxHashMap; +use collections::{FxHashMap, FxHashSet}; use derive_more::{Deref, DerefMut}; use futures::{ channel::{mpsc, oneshot}, @@ -256,6 +256,7 @@ pub struct Window { pub(crate) element_id_stack: GlobalElementId, pub(crate) rendered_frame: Frame, pub(crate) next_frame: Frame, + pub(crate) dirty_views: FxHashSet, frame_arena: Arena, pub(crate) focus_handles: Arc>>, focus_listeners: SubscriberSet<(), AnyWindowFocusListener>, @@ -295,6 +296,8 @@ pub(crate) struct Frame { pub(crate) next_stacking_order_id: u32, content_mask_stack: Vec>, element_offset_stack: Vec>, + pub(crate) view_parents: FxHashMap, + pub(crate) view_stack: Vec, } impl Frame { @@ -310,6 +313,8 @@ impl Frame { depth_map: Default::default(), content_mask_stack: Vec::new(), element_offset_stack: Vec::new(), + view_parents: FxHashMap::default(), + view_stack: Vec::new(), } } @@ -319,6 +324,8 @@ 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()); } fn focus_path(&self) -> SmallVec<[FocusId; 8]> { @@ -404,6 +411,7 @@ impl Window { element_id_stack: GlobalElementId::default(), rendered_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())), next_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())), + dirty_views: FxHashSet::default(), frame_arena: Arena::new(1024 * 1024), focus_handles: Arc::new(RwLock::new(SlotMap::with_key())), focus_listeners: SubscriberSet::new(), @@ -1423,6 +1431,7 @@ impl<'a> WindowContext<'a> { } self.window.drawing = false; + self.window.dirty_views.clear(); ELEMENT_ARENA.with_borrow_mut(|element_arena| element_arena.clear()); scene @@ -2119,6 +2128,13 @@ 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.view_stack.push(view_id); + let result = f(self); + self.window_mut().next_frame.view_stack.pop(); + result + } + /// Update the global element offset relative to the current offset. This is used to implement /// scrolling. fn with_element_offset( @@ -2476,6 +2492,21 @@ 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 { + break; + } + } + if !self.window.drawing { self.window_cx.notify(); self.window_cx.app.push_effect(Effect::Notify { From f55870f378f41e8f9d093a4a2f683590cd9b75d1 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 9 Jan 2024 12:37:24 +0100 Subject: [PATCH 04/22] Reuse mouse and keyboard listeners when reusing geometry for a view --- crates/gpui/src/key_dispatch.rs | 81 ++++++++++++++++++++--- crates/gpui/src/view.rs | 3 +- crates/gpui/src/window.rs | 113 ++++++++++++++++---------------- 3 files changed, 131 insertions(+), 66 deletions(-) diff --git a/crates/gpui/src/key_dispatch.rs b/crates/gpui/src/key_dispatch.rs index 22c4dffc03a78df8fde5530a3059887e91a2b876..9019670b04f4c66ea76761026a73a8fd6a0931dc 100644 --- a/crates/gpui/src/key_dispatch.rs +++ b/crates/gpui/src/key_dispatch.rs @@ -1,12 +1,13 @@ use crate::{ - arena::ArenaRef, Action, ActionRegistry, DispatchPhase, FocusId, KeyBinding, KeyContext, - KeyMatch, Keymap, Keystroke, KeystrokeMatcher, WindowContext, + Action, ActionRegistry, DispatchPhase, EntityId, FocusId, KeyBinding, KeyContext, KeyMatch, + Keymap, Keystroke, KeystrokeMatcher, WindowContext, }; -use collections::HashMap; +use collections::FxHashMap; use parking_lot::Mutex; use smallvec::SmallVec; use std::{ any::{Any, TypeId}, + mem, rc::Rc, sync::Arc, }; @@ -18,8 +19,9 @@ pub(crate) struct DispatchTree { node_stack: Vec, pub(crate) context_stack: Vec, nodes: Vec, - focusable_node_ids: HashMap, - keystroke_matchers: HashMap, KeystrokeMatcher>, + focusable_node_ids: FxHashMap, + view_node_ids: FxHashMap, + keystroke_matchers: FxHashMap, KeystrokeMatcher>, keymap: Arc>, action_registry: Rc, } @@ -30,15 +32,16 @@ pub(crate) struct DispatchNode { pub action_listeners: Vec, pub context: Option, focus_id: Option, + view_id: Option, parent: Option, } -type KeyListener = ArenaRef; +type KeyListener = Rc; #[derive(Clone)] pub(crate) struct DispatchActionListener { pub(crate) action_type: TypeId, - pub(crate) listener: ArenaRef, + pub(crate) listener: Rc, } impl DispatchTree { @@ -47,8 +50,9 @@ impl DispatchTree { node_stack: Vec::new(), context_stack: Vec::new(), nodes: Vec::new(), - focusable_node_ids: HashMap::default(), - keystroke_matchers: HashMap::default(), + focusable_node_ids: FxHashMap::default(), + view_node_ids: FxHashMap::default(), + keystroke_matchers: FxHashMap::default(), keymap, action_registry, } @@ -59,6 +63,7 @@ impl DispatchTree { self.nodes.clear(); self.context_stack.clear(); self.focusable_node_ids.clear(); + self.view_node_ids.clear(); self.keystroke_matchers.clear(); } @@ -83,6 +88,56 @@ impl DispatchTree { } } + fn move_node(&mut self, source_node: &mut DispatchNode) { + self.push_node(source_node.context.take()); + if let Some(focus_id) = source_node.focus_id { + self.make_focusable(focus_id); + } + if let Some(view_id) = source_node.view_id { + self.associate_view(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); + } + + pub fn graft(&mut self, view_id: EntityId, source: &mut Self) { + let view_source_node_id = source + .view_node_ids + .get(&view_id) + .expect("view should exist in previous dispatch tree"); + let view_source_node = &mut source.nodes[view_source_node_id.0]; + self.move_node(view_source_node); + + let mut source_stack = vec![*view_source_node_id]; + for (source_node_id, source_node) in source + .nodes + .iter_mut() + .enumerate() + .skip(view_source_node_id.0 + 1) + { + let source_node_id = DispatchNodeId(source_node_id); + while let Some(source_ancestor) = source_stack.last() { + if source_node.parent != Some(*source_ancestor) { + source_stack.pop(); + self.pop_node(); + } + } + + if source_stack.is_empty() { + break; + } else { + source_stack.push(source_node_id); + self.move_node(source_node); + } + } + + while !source_stack.is_empty() { + self.pop_node(); + } + } + pub fn clear_pending_keystrokes(&mut self) { self.keystroke_matchers.clear(); } @@ -117,7 +172,7 @@ impl DispatchTree { pub fn on_action( &mut self, action_type: TypeId, - listener: ArenaRef, + listener: Rc, ) { self.active_node() .action_listeners @@ -133,6 +188,12 @@ impl DispatchTree { self.focusable_node_ids.insert(focus_id, node_id); } + pub fn associate_view(&mut self, view_id: EntityId) { + let node_id = self.active_node_id(); + self.active_node().view_id = Some(view_id); + self.view_node_ids.insert(view_id, node_id); + } + pub fn focus_contains(&self, parent: FocusId, child: FocusId) -> bool { if parent == child { return true; diff --git a/crates/gpui/src/view.rs b/crates/gpui/src/view.rs index 9d504627ab185212df7df14399dfc54bcf1d6b46..24bc00ce39796890158bc6890e0fb779801a73b3 100644 --- a/crates/gpui/src/view.rs +++ b/crates/gpui/src/view.rs @@ -285,7 +285,8 @@ impl Element for AnyView { && cache_key.text_style == cx.text_style() && !cx.window.dirty_views.contains(&self.entity_id()) { - println!("could reuse geometry for view {}", self.entity_id()); + cx.reuse_geometry(); + return; } } diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 98c81d3883122066edef4254d90b8adb6ba0ad3c..e5f9195b692409ec176d40d6014a22b5ceaaa6f8 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -1,8 +1,8 @@ use crate::{ - px, size, transparent_black, Action, AnyDrag, AnyView, AppContext, Arena, ArenaBox, ArenaRef, - AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle, - DevicePixels, DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, - Entity, EntityId, EventEmitter, FileDropEvent, Flatten, FontId, GlobalElementId, GlyphId, Hsla, + px, size, transparent_black, Action, AnyDrag, AnyView, AppContext, Arena, AsyncWindowContext, + AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle, DevicePixels, + DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, + EntityId, EventEmitter, FileDropEvent, Flatten, FontId, GlobalElementId, GlyphId, Hsla, ImageData, InputEvent, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeystrokeEvent, LayoutId, Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, @@ -95,7 +95,7 @@ impl DispatchPhase { } type AnyObserver = Box bool + 'static>; -type AnyMouseListener = ArenaBox; +type AnyMouseListener = Box; type AnyWindowFocusListener = Box bool + 'static>; struct FocusEvent { @@ -257,7 +257,6 @@ pub struct Window { pub(crate) rendered_frame: Frame, pub(crate) next_frame: Frame, pub(crate) dirty_views: FxHashSet, - frame_arena: Arena, pub(crate) focus_handles: Arc>>, focus_listeners: SubscriberSet<(), AnyWindowFocusListener>, blur_listeners: SubscriberSet<(), AnyObserver>, @@ -288,7 +287,7 @@ pub(crate) struct ElementStateBox { pub(crate) struct Frame { focus: Option, pub(crate) element_states: FxHashMap, - mouse_listeners: FxHashMap>, + mouse_listeners: FxHashMap>, pub(crate) dispatch_tree: DispatchTree, pub(crate) scene_builder: SceneBuilder, pub(crate) depth_map: Vec<(StackingOrder, Bounds)>, @@ -298,6 +297,7 @@ pub(crate) struct Frame { element_offset_stack: Vec>, pub(crate) view_parents: FxHashMap, pub(crate) view_stack: Vec, + pub(crate) reused_views: FxHashSet, } impl Frame { @@ -315,6 +315,7 @@ impl Frame { element_offset_stack: Vec::new(), view_parents: FxHashMap::default(), view_stack: Vec::new(), + reused_views: FxHashSet::default(), } } @@ -326,6 +327,7 @@ impl Frame { self.next_stacking_order_id = 0; self.view_parents.clear(); debug_assert!(self.view_stack.is_empty()); + self.reused_views.clear(); } fn focus_path(&self) -> SmallVec<[FocusId; 8]> { @@ -412,7 +414,6 @@ impl Window { rendered_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())), next_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())), dirty_views: FxHashSet::default(), - frame_arena: Arena::new(1024 * 1024), focus_handles: Arc::new(RwLock::new(SlotMap::with_key())), focus_listeners: SubscriberSet::new(), blur_listeners: SubscriberSet::new(), @@ -886,21 +887,21 @@ impl<'a> WindowContext<'a> { mut handler: impl FnMut(&Event, DispatchPhase, &mut WindowContext) + 'static, ) { let order = self.window.next_frame.z_index_stack.clone(); - let handler = self - .window - .frame_arena - .alloc(|| { - move |event: &dyn Any, phase: DispatchPhase, cx: &mut WindowContext<'_>| { - handler(event.downcast_ref().unwrap(), phase, cx) - } - }) - .map(|handler| handler as _); + let view_id = *self.window.next_frame.view_stack.last().unwrap(); self.window .next_frame .mouse_listeners .entry(TypeId::of::()) .or_default() - .push((order, handler)) + .push(( + order, + view_id, + Box::new( + move |event: &dyn Any, phase: DispatchPhase, cx: &mut WindowContext<'_>| { + handler(event.downcast_ref().unwrap(), phase, cx) + }, + ), + )) } /// Register a key event listener on the window for the next frame. The type of event @@ -913,21 +914,13 @@ impl<'a> WindowContext<'a> { &mut self, listener: impl Fn(&Event, DispatchPhase, &mut WindowContext) + 'static, ) { - let listener = self - .window - .frame_arena - .alloc(|| { - move |event: &dyn Any, phase, cx: &mut WindowContext<'_>| { - if let Some(event) = event.downcast_ref::() { - listener(event, phase, cx) - } + self.window.next_frame.dispatch_tree.on_key_event(Rc::new( + move |event: &dyn Any, phase, cx: &mut WindowContext<'_>| { + if let Some(event) = event.downcast_ref::() { + listener(event, phase, cx) } - }) - .map(|handler| handler as _); - self.window - .next_frame - .dispatch_tree - .on_key_event(ArenaRef::from(listener)); + }, + )); } /// Register an action listener on the window for the next frame. The type of action @@ -941,15 +934,10 @@ impl<'a> WindowContext<'a> { action_type: TypeId, listener: impl Fn(&dyn Any, DispatchPhase, &mut WindowContext) + 'static, ) { - let listener = self - .window - .frame_arena - .alloc(|| listener) - .map(|handler| handler as _); self.window .next_frame .dispatch_tree - .on_action(action_type, ArenaRef::from(listener)); + .on_action(action_type, Rc::new(listener)); } pub fn is_action_available(&self, action: &dyn Action) -> bool { @@ -1327,6 +1315,16 @@ impl<'a> WindowContext<'a> { ); } + pub(crate) fn reuse_geometry(&mut self) { + 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 + .next_frame + .dispatch_tree + .graft(view_id, &mut window.rendered_frame.dispatch_tree) + } + /// Draw pixels to the display for this window based on the contents of its scene. pub(crate) fn draw(&mut self) -> Scene { println!("====================="); @@ -1342,26 +1340,18 @@ impl<'a> WindowContext<'a> { self.window.platform_window.clear_input_handler(); self.window.layout_engine.as_mut().unwrap().clear(); self.window.next_frame.clear(); - self.window.frame_arena.clear(); let root_view = self.window.root_view.take().unwrap(); self.with_z_index(0, |cx| { cx.with_key_dispatch(Some(KeyContext::default()), None, |_, cx| { for (action_type, action_listeners) in &cx.app.global_action_listeners { for action_listener in action_listeners.iter().cloned() { - let listener = cx - .window - .frame_arena - .alloc(|| { - move |action: &dyn Any, phase, cx: &mut WindowContext<'_>| { - action_listener(action, phase, cx) - } - }) - .map(|listener| listener as _); - cx.window - .next_frame - .dispatch_tree - .on_action(*action_type, ArenaRef::from(listener)) + cx.window.next_frame.dispatch_tree.on_action( + *action_type, + Rc::new(move |action: &dyn Any, phase, cx: &mut WindowContext<'_>| { + action_listener(action, phase, cx) + }), + ) } } @@ -1395,6 +1385,19 @@ 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 + .next_frame + .mouse_listeners + .entry(*type_id) + .or_default(); + for (order, view_id, listener) in listeners.drain(..) { + if self.window.next_frame.reused_views.contains(&view_id) { + next_listeners.push((order, view_id, listener)); + } + } + } let previous_focus_path = self.window.rendered_frame.focus_path(); mem::swap(&mut self.window.rendered_frame, &mut self.window.next_frame); @@ -1540,11 +1543,11 @@ impl<'a> WindowContext<'a> { .remove(&event.type_id()) { // Because handlers may add other handlers, we sort every time. - handlers.sort_by(|(a, _), (b, _)| a.cmp(b)); + handlers.sort_by(|(a, _, _), (b, _, _)| a.cmp(b)); // Capture phase, events bubble from back to front. Handlers for this phase are used for // special purposes, such as detecting events outside of a given Bounds. - for (_, handler) in &mut handlers { + for (_, _, handler) in &mut handlers { handler(event, DispatchPhase::Capture, self); if !self.app.propagate_event { break; @@ -1553,7 +1556,7 @@ impl<'a> WindowContext<'a> { // Bubble phase, where most normal handlers do their work. if self.app.propagate_event { - for (_, handler) in handlers.iter_mut().rev() { + for (_, _, handler) in handlers.iter_mut().rev() { handler(event, DispatchPhase::Bubble, self); if !self.app.propagate_event { break; From 84b05d6c0564948369383a953a65666597b082eb Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 9 Jan 2024 15:12:23 +0100 Subject: [PATCH 05/22] Maintain view stack as part of `DispatchTree` --- 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(-) diff --git a/crates/gpui/src/key_dispatch.rs b/crates/gpui/src/key_dispatch.rs index 9019670b04f4c66ea76761026a73a8fd6a0931dc..af836b7166635f85002a00b11b61a8643ab6b31c 100644 --- a/crates/gpui/src/key_dispatch.rs +++ b/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, pub(crate) context_stack: Vec, + view_stack: Vec, nodes: Vec, focusable_node_ids: FxHashMap, view_node_ids: FxHashMap, @@ -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 { + self.view_stack.last().copied() + } + pub fn node(&self, node_id: DispatchNodeId) -> &DispatchNode { &self.nodes[node_id.0] } diff --git a/crates/gpui/src/scene.rs b/crates/gpui/src/scene.rs index e922c11f533680e102e67bfa7bd5d51b741678a3..42183100b7c63ae2c5959a2b885afb3ba626d219 100644 --- a/crates/gpui/src/scene.rs +++ b/crates/gpui/src/scene.rs @@ -11,13 +11,12 @@ pub(crate) type PointF = Point; pub(crate) type PathVertex_ScaledPixels = PathVertex; pub type LayerId = u32; - pub type DrawOrder = u32; #[derive(Default)] pub(crate) struct SceneBuilder { - last_order: Option<(StackingOrder, LayerId)>, layers_by_order: BTreeMap, + orders_by_layer: BTreeMap, shadows: Vec, quads: Vec, paths: Vec>, @@ -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, pub content_mask: ContentMask, pub background: Hsla, @@ -469,7 +462,9 @@ impl From 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, pub content_mask: ContentMask, pub thickness: ScaledPixels, @@ -498,7 +493,9 @@ impl From 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, pub corner_radii: Corners, pub content_mask: ContentMask, @@ -527,7 +524,9 @@ impl From 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, pub content_mask: ContentMask, pub color: Hsla, @@ -558,7 +557,9 @@ impl From 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, pub content_mask: ContentMask, pub corner_radii: Corners, @@ -589,7 +590,9 @@ impl From 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, pub content_mask: ContentMask, pub image_buffer: media::core_video::CVImageBuffer, @@ -619,7 +622,9 @@ pub(crate) struct PathId(pub(crate) usize); #[derive(Debug)] pub struct Path { pub(crate) id: PathId, - order: u32, + pub(crate) view_id: u32, + layer_id: LayerId, + order: DrawOrder, pub(crate) bounds: Bounds

, pub(crate) content_mask: ContentMask

, pub(crate) vertices: Vec>, @@ -633,6 +638,8 @@ impl Path { pub fn new(start: Point) -> Self { Self { id: PathId(0), + view_id: 0, + layer_id: 0, order: 0, vertices: Vec::new(), start, @@ -650,6 +657,8 @@ impl Path { pub fn scale(&self, factor: f32) -> Path { 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), diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index e5f9195b692409ec176d40d6014a22b5ceaaa6f8..f21b8b3b80d691f2707f4e2c18f2213809c40d36 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -295,8 +295,6 @@ pub(crate) struct Frame { pub(crate) next_stacking_order_id: u32, content_mask_stack: Vec>, element_offset_stack: Vec>, - pub(crate) view_parents: FxHashMap, - pub(crate) view_stack: Vec, pub(crate) reused_views: FxHashSet, } @@ -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, color: impl Into) { 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 + BorrowMut { } fn with_view_id(&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; } } From 3bb29acd26f9d406cbbe6660fb361485792909ec Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 9 Jan 2024 17:34:57 +0100 Subject: [PATCH 06/22] :lipstick: --- crates/gpui/src/key_dispatch.rs | 45 +++++++++++++++++---------------- crates/gpui/src/window.rs | 16 +++++------- 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/crates/gpui/src/key_dispatch.rs b/crates/gpui/src/key_dispatch.rs index af836b7166635f85002a00b11b61a8643ab6b31c..c6abc101eabd398ce3beafe7f7c0da85702174e2 100644 --- a/crates/gpui/src/key_dispatch.rs +++ b/crates/gpui/src/key_dispatch.rs @@ -70,18 +70,35 @@ impl DispatchTree { self.keystroke_matchers.clear(); } - pub fn push_node(&mut self, context: Option) { + pub fn push_node( + &mut self, + context: Option, + focus_id: Option, + view_id: Option, + ) { let parent = self.node_stack.last().copied(); let node_id = DispatchNodeId(self.nodes.len()); self.nodes.push(DispatchNode { parent, + focus_id, + view_id, ..Default::default() }); self.node_stack.push(node_id); + if let Some(context) = context { self.active_node().context = Some(context.clone()); self.context_stack.push(context); } + + if let Some(focus_id) = focus_id { + self.focusable_node_ids.insert(focus_id, node_id); + } + + if let Some(view_id) = view_id { + self.view_stack.push(view_id); + self.view_node_ids.insert(view_id, node_id); + } } pub fn pop_node(&mut self) { @@ -96,14 +113,11 @@ impl DispatchTree { } fn move_node(&mut self, source_node: &mut DispatchNode) { - self.push_node(source_node.context.take()); - if let Some(focus_id) = source_node.focus_id { - self.make_focusable(focus_id); - } - if let Some(view_id) = source_node.view_id { - self.associate_view(view_id); - } - + 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); @@ -195,19 +209,6 @@ impl DispatchTree { }); } - pub fn make_focusable(&mut self, focus_id: FocusId) { - let node_id = self.active_node_id(); - self.active_node().focus_id = Some(focus_id); - self.focusable_node_ids.insert(focus_id, node_id); - } - - pub fn associate_view(&mut self, view_id: EntityId) { - 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 { if parent == child { return true; diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index f21b8b3b80d691f2707f4e2c18f2213809c40d36..2748ebed8489a35025a89ac35ab22311522e8c28 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -1866,13 +1866,12 @@ impl<'a> WindowContext<'a> { f: impl FnOnce(Option, &mut Self) -> R, ) -> R { let window = &mut self.window; - window.next_frame.dispatch_tree.push_node(context.clone()); - if let Some(focus_handle) = focus_handle.as_ref() { - window - .next_frame - .dispatch_tree - .make_focusable(focus_handle.id); - } + let focus_id = focus_handle.as_ref().map(|handle| handle.id); + window + .next_frame + .dispatch_tree + .push_node(context.clone(), focus_id, None); + let result = f(focus_handle, self); self.window.next_frame.dispatch_tree.pop_node(); @@ -2165,11 +2164,10 @@ pub trait BorrowWindow: BorrowMut + BorrowMut { } 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); self.window_mut() .next_frame .dispatch_tree - .associate_view(view_id); + .push_node(None, None, Some(view_id)); let result = f(self); self.window_mut().next_frame.dispatch_tree.pop_node(); result From 0c6d107740cded0b78b291db25346867c8bf27af Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 9 Jan 2024 18:00:48 +0100 Subject: [PATCH 07/22] Introduce `on_request_frame` --- crates/gpui/src/platform.rs | 5 ++-- crates/gpui/src/platform/mac/platform.rs | 11 ++------ crates/gpui/src/platform/mac/window.rs | 32 +++++++++++++---------- crates/gpui/src/platform/test/platform.rs | 4 +-- crates/gpui/src/platform/test/window.rs | 8 +++--- crates/gpui/src/window.rs | 19 +++++++------- 6 files changed, 37 insertions(+), 42 deletions(-) diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 0ef345d98d1de34bbb7c6b567b4716608e2e6dbf..c41bf998f698443d5b6ca93dee2966206596d533 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -44,8 +44,6 @@ pub(crate) fn current_platform() -> Rc { Rc::new(MacPlatform::new()) } -pub type DrawWindow = Box Result>; - pub(crate) trait Platform: 'static { fn background_executor(&self) -> BackgroundExecutor; fn foreground_executor(&self) -> ForegroundExecutor; @@ -66,7 +64,6 @@ pub(crate) trait Platform: 'static { &self, handle: AnyWindowHandle, options: WindowOptions, - draw: DrawWindow, ) -> Box; fn set_display_link_output_callback( @@ -157,6 +154,7 @@ pub trait PlatformWindow { fn minimize(&self); fn zoom(&self); fn toggle_full_screen(&self); + fn on_request_frame(&self, callback: Box); fn on_input(&self, callback: Box bool>); fn on_active_status_change(&self, callback: Box); fn on_resize(&self, callback: Box, f32)>); @@ -167,6 +165,7 @@ pub trait PlatformWindow { fn on_appearance_changed(&self, callback: Box); fn is_topmost_for_position(&self, position: Point) -> bool; fn invalidate(&self); + fn draw(&self, scene: &Scene); fn sprite_atlas(&self) -> Arc; diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 8370e2a4953c1280a59d4a9cb74a93ae97214db2..78c2a0738127e43f36d48f60553a1f854b8ec761 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -3,8 +3,7 @@ use crate::{ Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, ForegroundExecutor, InputEvent, Keymap, MacDispatcher, MacDisplay, MacDisplayLinker, MacTextSystem, MacWindow, Menu, MenuItem, PathPromptOptions, Platform, PlatformDisplay, - PlatformTextSystem, PlatformWindow, Result, Scene, SemanticVersion, VideoTimestamp, - WindowOptions, + PlatformTextSystem, PlatformWindow, Result, SemanticVersion, VideoTimestamp, WindowOptions, }; use anyhow::anyhow; use block::ConcreteBlock; @@ -498,14 +497,8 @@ impl Platform for MacPlatform { &self, handle: AnyWindowHandle, options: WindowOptions, - draw: Box Result>, ) -> Box { - Box::new(MacWindow::open( - handle, - options, - draw, - self.foreground_executor(), - )) + Box::new(MacWindow::open(handle, options, self.foreground_executor())) } fn set_display_link_output_callback( diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index 2beac528c18f53cfa9a39b008dbebf3825502b30..6f15a7d1acddad42b3427ddf2129d26b4519dd92 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -1,6 +1,6 @@ use super::{display_bounds_from_native, ns_string, MacDisplay, MetalRenderer, NSRange}; use crate::{ - display_bounds_to_native, point, px, size, AnyWindowHandle, Bounds, DrawWindow, ExternalPaths, + display_bounds_to_native, point, px, size, AnyWindowHandle, Bounds, ExternalPaths, FileDropEvent, ForegroundExecutor, GlobalPixels, InputEvent, KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, @@ -46,7 +46,6 @@ use std::{ sync::{Arc, Weak}, time::Duration, }; -use util::ResultExt; const WINDOW_STATE_IVAR: &str = "windowState"; @@ -317,8 +316,8 @@ struct MacWindowState { executor: ForegroundExecutor, native_window: id, renderer: MetalRenderer, - draw: Option, kind: WindowKind, + request_frame_callback: Option>, event_callback: Option bool>>, activate_callback: Option>, resize_callback: Option, f32)>>, @@ -453,7 +452,6 @@ impl MacWindow { pub fn open( handle: AnyWindowHandle, options: WindowOptions, - draw: DrawWindow, executor: ForegroundExecutor, ) -> Self { unsafe { @@ -545,8 +543,8 @@ impl MacWindow { executor, native_window, renderer: MetalRenderer::new(true), - draw: Some(draw), kind: options.kind, + request_frame_callback: None, event_callback: None, activate_callback: None, resize_callback: None, @@ -926,6 +924,10 @@ impl PlatformWindow for MacWindow { .detach(); } + fn on_request_frame(&self, callback: Box) { + self.0.as_ref().lock().request_frame_callback = Some(callback); + } + fn on_input(&self, callback: Box bool>) { self.0.as_ref().lock().event_callback = Some(callback); } @@ -990,6 +992,11 @@ impl PlatformWindow for MacWindow { } } + fn draw(&self, scene: &crate::Scene) { + let mut this = self.0.lock(); + this.renderer.draw(scene); + } + fn sprite_atlas(&self) -> Arc { self.0.lock().renderer.sprite_atlas().clone() } @@ -1462,15 +1469,12 @@ extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) { } extern "C" fn display_layer(this: &Object, _: Sel, _: id) { - unsafe { - let window_state = get_window_state(this); - let mut draw = window_state.lock().draw.take().unwrap(); - let scene = draw().log_err(); - let mut window_state = window_state.lock(); - window_state.draw = Some(draw); - if let Some(scene) = scene { - window_state.renderer.draw(&scene); - } + let window_state = unsafe { get_window_state(this) }; + let mut lock = window_state.lock(); + if let Some(mut callback) = lock.request_frame_callback.take() { + drop(lock); + callback(); + window_state.lock().request_frame_callback = Some(callback); } } diff --git a/crates/gpui/src/platform/test/platform.rs b/crates/gpui/src/platform/test/platform.rs index 695323e9c46b8e2a8f4260a682d8e214f58c43f4..5067f6a4b3417853a9fe36b59681ddfc5984437f 100644 --- a/crates/gpui/src/platform/test/platform.rs +++ b/crates/gpui/src/platform/test/platform.rs @@ -1,7 +1,6 @@ use crate::{ AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, ForegroundExecutor, - Keymap, Platform, PlatformDisplay, PlatformTextSystem, Scene, TestDisplay, TestWindow, - WindowOptions, + Keymap, Platform, PlatformDisplay, PlatformTextSystem, TestDisplay, TestWindow, WindowOptions, }; use anyhow::{anyhow, Result}; use collections::VecDeque; @@ -162,7 +161,6 @@ impl Platform for TestPlatform { &self, handle: AnyWindowHandle, options: WindowOptions, - _draw: Box Result>, ) -> Box { let window = TestWindow::new( options, diff --git a/crates/gpui/src/platform/test/window.rs b/crates/gpui/src/platform/test/window.rs index 91f965c10ac2987f4f6c8c15cb40a7cd94171370..029fd7300998f55f089696d6e01199b9c687ddfd 100644 --- a/crates/gpui/src/platform/test/window.rs +++ b/crates/gpui/src/platform/test/window.rs @@ -218,6 +218,8 @@ impl PlatformWindow for TestWindow { unimplemented!() } + fn on_request_frame(&self, _callback: Box) {} + fn on_input(&self, callback: Box bool>) { self.0.lock().input_callback = Some(callback) } @@ -254,9 +256,9 @@ impl PlatformWindow for TestWindow { unimplemented!() } - fn invalidate(&self) { - // (self.draw.lock())().unwrap(); - } + fn invalidate(&self) {} + + fn draw(&self, _scene: &crate::Scene) {} fn sprite_atlas(&self) -> sync::Arc { self.0.lock().sprite_atlas.clone() diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 2748ebed8489a35025a89ac35ab22311522e8c28..8f9a82672da6a3c006d788a3e10782403f7c0a17 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -337,14 +337,7 @@ impl Window { options: WindowOptions, cx: &mut AppContext, ) -> Self { - let platform_window = cx.platform.open_window( - handle, - options, - Box::new({ - let mut cx = cx.to_async(); - move || handle.update(&mut cx, |_, cx| cx.draw()) - }), - ); + let platform_window = cx.platform.open_window(handle, options); let display_id = platform_window.display().id(); let sprite_atlas = platform_window.sprite_atlas(); let mouse_position = platform_window.mouse_position(); @@ -353,6 +346,12 @@ impl Window { let scale_factor = platform_window.scale_factor(); let bounds = platform_window.bounds(); + platform_window.on_request_frame(Box::new({ + let mut cx = cx.to_async(); + move || { + handle.update(&mut cx, |_, cx| cx.draw()).log_err(); + } + })); platform_window.on_resize(Box::new({ let mut cx = cx.to_async(); move |_, _| { @@ -1358,7 +1357,7 @@ impl<'a> WindowContext<'a> { } /// Draw pixels to the display for this window based on the contents of its scene. - pub(crate) fn draw(&mut self) -> Scene { + pub(crate) fn draw(&mut self) { println!("====================="); self.window.dirty = false; self.window.drawing = true; @@ -1470,7 +1469,7 @@ impl<'a> WindowContext<'a> { self.window.drawing = false; ELEMENT_ARENA.with_borrow_mut(|element_arena| element_arena.clear()); - scene + self.window.platform_window.draw(&scene); } /// Dispatch a mouse or keyboard event on the window. From 881c532256d4263edc55251e05b163f38de915f6 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 10 Jan 2024 10:50:16 +0100 Subject: [PATCH 08/22] Insert primitives associated with views from a previous scene --- crates/gpui/src/app/entity_map.rs | 8 +- crates/gpui/src/key_dispatch.rs | 3 + crates/gpui/src/scene.rs | 218 ++++++++++++++++++------------ crates/gpui/src/view.rs | 2 +- crates/gpui/src/window.rs | 41 +++--- 5 files changed, 164 insertions(+), 108 deletions(-) diff --git a/crates/gpui/src/app/entity_map.rs b/crates/gpui/src/app/entity_map.rs index 17f6e47ddfec85de1c82b1fc0b8cd6ffc285aa32..482a003fe76fdbab46b8447efff2610ffaec52e3 100644 --- a/crates/gpui/src/app/entity_map.rs +++ b/crates/gpui/src/app/entity_map.rs @@ -2,7 +2,7 @@ use crate::{seal::Sealed, AppContext, Context, Entity, ModelContext}; use anyhow::{anyhow, Result}; use derive_more::{Deref, DerefMut}; use parking_lot::{RwLock, RwLockUpgradableReadGuard}; -use slotmap::{SecondaryMap, SlotMap}; +use slotmap::{KeyData, SecondaryMap, SlotMap}; use std::{ any::{type_name, Any, TypeId}, fmt::{self, Display}, @@ -21,6 +21,12 @@ use collections::HashMap; slotmap::new_key_type! { pub struct EntityId; } +impl From for EntityId { + fn from(value: u64) -> Self { + Self(KeyData::from_ffi(value)) + } +} + impl EntityId { pub fn as_u64(self) -> u64 { self.0.as_ffi() diff --git a/crates/gpui/src/key_dispatch.rs b/crates/gpui/src/key_dispatch.rs index c6abc101eabd398ce3beafe7f7c0da85702174e2..168b1da9fe7e54d9a36982deaed8972dd8b5c482 100644 --- a/crates/gpui/src/key_dispatch.rs +++ b/crates/gpui/src/key_dispatch.rs @@ -144,6 +144,8 @@ impl DispatchTree { if source_node.parent != Some(*source_ancestor) { source_stack.pop(); self.pop_node(); + } else { + break; } } @@ -159,6 +161,7 @@ impl DispatchTree { } while !source_stack.is_empty() { + source_stack.pop(); self.pop_node(); } diff --git a/crates/gpui/src/scene.rs b/crates/gpui/src/scene.rs index 42183100b7c63ae2c5959a2b885afb3ba626d219..9c63f803254b8f099be1cb7c369b41458a253df2 100644 --- a/crates/gpui/src/scene.rs +++ b/crates/gpui/src/scene.rs @@ -1,9 +1,9 @@ use crate::{ - point, AtlasTextureId, AtlasTile, Bounds, ContentMask, Corners, Edges, Hsla, Pixels, Point, - ScaledPixels, StackingOrder, + point, AtlasTextureId, AtlasTile, Bounds, ContentMask, Corners, Edges, EntityId, Hsla, Pixels, + Point, ScaledPixels, StackingOrder, }; -use collections::BTreeMap; -use std::{fmt::Debug, iter::Peekable, mem, slice}; +use collections::{BTreeMap, FxHashSet}; +use std::{fmt::Debug, iter::Peekable, slice}; // Exported to metal pub(crate) type PointF = Point; @@ -14,7 +14,7 @@ pub type LayerId = u32; pub type DrawOrder = u32; #[derive(Default)] -pub(crate) struct SceneBuilder { +pub struct Scene { layers_by_order: BTreeMap, orders_by_layer: BTreeMap, shadows: Vec, @@ -26,57 +26,46 @@ pub(crate) struct SceneBuilder { surfaces: Vec, } -impl SceneBuilder { - pub fn build(&mut self) -> Scene { - let mut orders = vec![0; self.layers_by_order.len()]; - for (ix, layer_id) in self.layers_by_order.values().enumerate() { - orders[*layer_id as usize] = ix as u32; - } +impl Scene { + pub fn clear(&mut self) { self.layers_by_order.clear(); + self.orders_by_layer.clear(); + self.shadows.clear(); + self.quads.clear(); + self.paths.clear(); + self.underlines.clear(); + self.monochrome_sprites.clear(); + self.polychrome_sprites.clear(); + self.surfaces.clear(); + } - for shadow in &mut self.shadows { - 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.layer_id as usize]; - } - self.quads.sort_by_key(|quad| quad.order); - - for path in &mut self.paths { - 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.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.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.layer_id as usize]; - } - self.polychrome_sprites.sort_by_key(|sprite| sprite.order); - - for surface in &mut self.surfaces { - surface.order = orders[surface.layer_id as usize]; - } - self.surfaces.sort_by_key(|surface| surface.order); + pub fn paths(&self) -> &[Path] { + &self.paths + } - Scene { - shadows: mem::take(&mut self.shadows), - quads: mem::take(&mut self.quads), - paths: mem::take(&mut self.paths), - underlines: mem::take(&mut self.underlines), - monochrome_sprites: mem::take(&mut self.monochrome_sprites), - polychrome_sprites: mem::take(&mut self.polychrome_sprites), - surfaces: mem::take(&mut self.surfaces), + pub(crate) fn batches(&self) -> impl Iterator { + BatchIterator { + shadows: &self.shadows, + shadows_start: 0, + shadows_iter: self.shadows.iter().peekable(), + quads: &self.quads, + quads_start: 0, + quads_iter: self.quads.iter().peekable(), + paths: &self.paths, + paths_start: 0, + paths_iter: self.paths.iter().peekable(), + underlines: &self.underlines, + underlines_start: 0, + underlines_iter: self.underlines.iter().peekable(), + monochrome_sprites: &self.monochrome_sprites, + monochrome_sprites_start: 0, + monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(), + polychrome_sprites: &self.polychrome_sprites, + polychrome_sprites_start: 0, + polychrome_sprites_iter: self.polychrome_sprites.iter().peekable(), + surfaces: &self.surfaces, + surfaces_start: 0, + surfaces_iter: self.surfaces.iter().peekable(), } } @@ -135,47 +124,98 @@ impl SceneBuilder { next_id } } -} -pub struct Scene { - pub shadows: Vec, - pub quads: Vec, - pub paths: Vec>, - pub underlines: Vec, - pub monochrome_sprites: Vec, - pub polychrome_sprites: Vec, - pub surfaces: Vec, -} + pub fn insert_views_from_scene(&mut self, views: &FxHashSet, prev_scene: &mut Self) { + for shadow in prev_scene.shadows.drain(..) { + if views.contains(&EntityId::from(shadow.view_id as u64)) { + let order = &prev_scene.orders_by_layer[&shadow.layer_id]; + self.insert(&order, shadow); + } + } -impl Scene { - pub fn paths(&self) -> &[Path] { - &self.paths + for quad in prev_scene.quads.drain(..) { + if views.contains(&EntityId::from(quad.view_id as u64)) { + let order = &prev_scene.orders_by_layer[&quad.layer_id]; + self.insert(&order, quad); + } + } + + for path in prev_scene.paths.drain(..) { + if views.contains(&EntityId::from(path.view_id as u64)) { + let order = &prev_scene.orders_by_layer[&path.layer_id]; + self.insert(&order, path); + } + } + + for underline in prev_scene.underlines.drain(..) { + if views.contains(&EntityId::from(underline.view_id as u64)) { + let order = &prev_scene.orders_by_layer[&underline.layer_id]; + self.insert(&order, underline); + } + } + + for sprite in prev_scene.monochrome_sprites.drain(..) { + if views.contains(&EntityId::from(sprite.view_id as u64)) { + let order = &prev_scene.orders_by_layer[&sprite.layer_id]; + self.insert(&order, sprite); + } + } + + for sprite in prev_scene.polychrome_sprites.drain(..) { + if views.contains(&EntityId::from(sprite.view_id as u64)) { + let order = &prev_scene.orders_by_layer[&sprite.layer_id]; + self.insert(&order, sprite); + } + } + + for surface in prev_scene.surfaces.drain(..) { + if views.contains(&EntityId::from(surface.view_id as u64)) { + let order = &prev_scene.orders_by_layer[&surface.layer_id]; + self.insert(&order, surface); + } + } } - pub(crate) fn batches(&self) -> impl Iterator { - BatchIterator { - shadows: &self.shadows, - shadows_start: 0, - shadows_iter: self.shadows.iter().peekable(), - quads: &self.quads, - quads_start: 0, - quads_iter: self.quads.iter().peekable(), - paths: &self.paths, - paths_start: 0, - paths_iter: self.paths.iter().peekable(), - underlines: &self.underlines, - underlines_start: 0, - underlines_iter: self.underlines.iter().peekable(), - monochrome_sprites: &self.monochrome_sprites, - monochrome_sprites_start: 0, - monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(), - polychrome_sprites: &self.polychrome_sprites, - polychrome_sprites_start: 0, - polychrome_sprites_iter: self.polychrome_sprites.iter().peekable(), - surfaces: &self.surfaces, - surfaces_start: 0, - surfaces_iter: self.surfaces.iter().peekable(), + pub fn finish(&mut self) { + let mut orders = vec![0; self.layers_by_order.len()]; + for (ix, layer_id) in self.layers_by_order.values().enumerate() { + orders[*layer_id as usize] = ix as u32; + } + + for shadow in &mut self.shadows { + 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.layer_id as usize]; + } + self.quads.sort_by_key(|quad| quad.order); + + for path in &mut self.paths { + 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.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.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.layer_id as usize]; + } + self.polychrome_sprites.sort_by_key(|sprite| sprite.order); + + for surface in &mut self.surfaces { + surface.order = orders[surface.layer_id as usize]; + } + self.surfaces.sort_by_key(|surface| surface.order); } } diff --git a/crates/gpui/src/view.rs b/crates/gpui/src/view.rs index 24bc00ce39796890158bc6890e0fb779801a73b3..d049c472587fed22606e144bccc4d333ffed6558 100644 --- a/crates/gpui/src/view.rs +++ b/crates/gpui/src/view.rs @@ -231,7 +231,7 @@ impl AnyView { 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); + cx.with_view_id(self.entity_id(), |cx| rendered_element.paint(cx)); }) } } diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 8f9a82672da6a3c006d788a3e10782403f7c0a17..45dc197e18ae07f1234f86c01d6558d768b890c6 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -7,9 +7,9 @@ use crate::{ Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams, - RenderSvgParams, ScaledPixels, Scene, SceneBuilder, Shadow, SharedString, Size, Style, - SubscriberSet, Subscription, Surface, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, - VisualContext, WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS, + RenderSvgParams, ScaledPixels, Scene, Shadow, SharedString, Size, Style, SubscriberSet, + Subscription, Surface, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext, + WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::{anyhow, Context as _, Result}; use collections::{FxHashMap, FxHashSet}; @@ -289,7 +289,7 @@ pub(crate) struct Frame { pub(crate) element_states: FxHashMap, mouse_listeners: FxHashMap>, pub(crate) dispatch_tree: DispatchTree, - pub(crate) scene_builder: SceneBuilder, + pub(crate) scene: Scene, pub(crate) depth_map: Vec<(StackingOrder, Bounds)>, pub(crate) z_index_stack: StackingOrder, pub(crate) next_stacking_order_id: u32, @@ -305,7 +305,7 @@ impl Frame { element_states: FxHashMap::default(), mouse_listeners: FxHashMap::default(), dispatch_tree, - scene_builder: SceneBuilder::default(), + scene: Scene::default(), z_index_stack: StackingOrder::default(), next_stacking_order_id: 0, depth_map: Default::default(), @@ -322,6 +322,7 @@ impl Frame { self.depth_map.clear(); self.next_stacking_order_id = 0; self.reused_views.clear(); + self.scene.clear(); } fn focus_path(&self) -> SmallVec<[FocusId; 8]> { @@ -1028,7 +1029,7 @@ impl<'a> WindowContext<'a> { let mut shadow_bounds = bounds; shadow_bounds.origin += shadow.offset; shadow_bounds.dilate(shadow.spread_radius); - window.next_frame.scene_builder.insert( + window.next_frame.scene.insert( &window.next_frame.z_index_stack, Shadow { view_id: view_id.as_u64() as u32, @@ -1053,7 +1054,7 @@ impl<'a> WindowContext<'a> { let view_id = self.active_view_id(); let window = &mut *self.window; - window.next_frame.scene_builder.insert( + window.next_frame.scene.insert( &window.next_frame.z_index_stack, Quad { view_id: view_id.as_u64() as u32, @@ -1081,7 +1082,7 @@ impl<'a> WindowContext<'a> { let window = &mut *self.window; window .next_frame - .scene_builder + .scene .insert(&window.next_frame.z_index_stack, path.scale(scale_factor)); } @@ -1106,7 +1107,7 @@ impl<'a> WindowContext<'a> { let view_id = self.active_view_id(); let window = &mut *self.window; - window.next_frame.scene_builder.insert( + window.next_frame.scene.insert( &window.next_frame.z_index_stack, Underline { view_id: view_id.as_u64() as u32, @@ -1162,7 +1163,7 @@ impl<'a> WindowContext<'a> { 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.scene.insert( &window.next_frame.z_index_stack, MonochromeSprite { view_id: view_id.as_u64() as u32, @@ -1216,7 +1217,7 @@ impl<'a> WindowContext<'a> { let view_id = self.active_view_id(); let window = &mut *self.window; - window.next_frame.scene_builder.insert( + window.next_frame.scene.insert( &window.next_frame.z_index_stack, PolychromeSprite { view_id: view_id.as_u64() as u32, @@ -1261,7 +1262,7 @@ impl<'a> WindowContext<'a> { let view_id = self.active_view_id(); let window = &mut *self.window; - window.next_frame.scene_builder.insert( + window.next_frame.scene.insert( &window.next_frame.z_index_stack, MonochromeSprite { view_id: view_id.as_u64() as u32, @@ -1300,7 +1301,7 @@ impl<'a> WindowContext<'a> { let view_id = self.active_view_id(); let window = &mut *self.window; - window.next_frame.scene_builder.insert( + window.next_frame.scene.insert( &window.next_frame.z_index_stack, PolychromeSprite { view_id: view_id.as_u64() as u32, @@ -1323,7 +1324,7 @@ impl<'a> WindowContext<'a> { 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.scene.insert( &window.next_frame.z_index_stack, Surface { view_id: view_id.as_u64() as u32, @@ -1337,6 +1338,7 @@ impl<'a> WindowContext<'a> { } pub(crate) fn reuse_geometry(&mut self) { + println!("reusing geometry"); let view_id = self.active_view_id(); let window = &mut self.window; let grafted_view_ids = window @@ -1407,6 +1409,11 @@ 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 @@ -1454,8 +1461,6 @@ impl<'a> WindowContext<'a> { .retain(&(), |listener| listener(&event, self)); } - let scene = self.window.rendered_frame.scene_builder.build(); - // Set the cursor only if we're the active window. let cursor_style = self .window @@ -1469,7 +1474,9 @@ impl<'a> WindowContext<'a> { self.window.drawing = false; ELEMENT_ARENA.with_borrow_mut(|element_arena| element_arena.clear()); - self.window.platform_window.draw(&scene); + self.window + .platform_window + .draw(&self.window.rendered_frame.scene); } /// Dispatch a mouse or keyboard event on the window. From d0c101cb6e5147ca9688e30afa34a26400c57fae Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 10 Jan 2024 15:00:40 +0100 Subject: [PATCH 09/22] Reuse depth map entries and retain element states for cached trees --- crates/gpui/src/key_dispatch.rs | 34 ++-- crates/gpui/src/view.rs | 46 ++--- crates/gpui/src/window.rs | 307 ++++++++++++++++++-------------- 3 files changed, 211 insertions(+), 176 deletions(-) diff --git a/crates/gpui/src/key_dispatch.rs b/crates/gpui/src/key_dispatch.rs index 168b1da9fe7e54d9a36982deaed8972dd8b5c482..a4a048a930fee5bec2918d54787416c7aa6c9ddc 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 d049c472587fed22606e144bccc4d333ffed6558..6ec78e0d2e8bd62dc5e30d7a47ba77cd254cb892 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 45dc197e18ae07f1234f86c01d6558d768b890c6..376f4c24662f39a766c9aaa3ce3b2c69a4f75f9c 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() From 2923b71f83cfdc2d5168b683d784095b80ff27ba Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 10 Jan 2024 17:27:02 +0100 Subject: [PATCH 10/22] Replace `WindowContext::notify` with `WindowContext::refresh` --- crates/copilot/src/sign_in.rs | 2 +- crates/gpui/src/elements/div.rs | 24 ++++++++++---------- crates/gpui/src/elements/img.rs | 2 +- crates/gpui/src/elements/list.rs | 2 +- crates/gpui/src/elements/text.rs | 4 ++-- crates/gpui/src/view.rs | 1 + crates/gpui/src/window.rs | 20 +++++++++------- crates/terminal_view/src/terminal_element.rs | 2 +- crates/ui/src/components/popover_menu.rs | 2 +- crates/ui/src/components/right_click_menu.rs | 4 ++-- crates/workspace/src/pane_group.rs | 2 +- 11 files changed, 35 insertions(+), 30 deletions(-) diff --git a/crates/copilot/src/sign_in.rs b/crates/copilot/src/sign_in.rs index ba5dbe0e315828ac1761cf1d76ac37bb8211ea4c..2ca31c7d332f7e5c6a32bf85b93d79dd2ee714d9 100644 --- a/crates/copilot/src/sign_in.rs +++ b/crates/copilot/src/sign_in.rs @@ -109,7 +109,7 @@ impl CopilotCodeVerification { let user_code = data.user_code.clone(); move |_, cx| { cx.write_to_clipboard(ClipboardItem::new(user_code.clone())); - cx.notify(); + cx.refresh(); } }) .child(Label::new(data.user_code.clone())) diff --git a/crates/gpui/src/elements/div.rs b/crates/gpui/src/elements/div.rs index 45097411d13875c7b1a8fecc4888711dbc0d9dd3..d750d692f0d54c18f4e10ae92693920cec1825f6 100644 --- a/crates/gpui/src/elements/div.rs +++ b/crates/gpui/src/elements/div.rs @@ -1027,7 +1027,7 @@ impl Interactivity { if e.modifiers.command != command_held && text_bounds.contains(&cx.mouse_position()) { - cx.notify(); + cx.refresh(); } } }); @@ -1038,7 +1038,7 @@ impl Interactivity { if phase == DispatchPhase::Capture && bounds.contains(&event.position) != hovered { - cx.notify(); + cx.refresh(); } }, ); @@ -1188,7 +1188,7 @@ impl Interactivity { if phase == DispatchPhase::Capture && group_bounds.contains(&event.position) != hovered { - cx.notify(); + cx.refresh(); } }); } @@ -1203,7 +1203,7 @@ impl Interactivity { if phase == DispatchPhase::Capture && bounds.contains(&event.position) != hovered { - cx.notify(); + cx.refresh(); } }); } @@ -1237,7 +1237,7 @@ impl Interactivity { if can_drop { listener(drag.value.as_ref(), cx); - cx.notify(); + cx.refresh(); cx.stop_propagation(); } } @@ -1268,7 +1268,7 @@ impl Interactivity { && interactive_bounds.visibly_contains(&event.position, cx) { *pending_mouse_down.borrow_mut() = Some(event.clone()); - cx.notify(); + cx.refresh(); } } }); @@ -1299,7 +1299,7 @@ impl Interactivity { cursor_offset, }); pending_mouse_down.take(); - cx.notify(); + cx.refresh(); cx.stop_propagation(); } } @@ -1319,7 +1319,7 @@ impl Interactivity { pending_mouse_down.borrow_mut(); if pending_mouse_down.is_some() { captured_mouse_down = pending_mouse_down.take(); - cx.notify(); + cx.refresh(); } } // Fire click handlers during the bubble phase. @@ -1413,7 +1413,7 @@ impl Interactivity { _task: None, }, ); - cx.notify(); + cx.refresh(); }) .ok(); } @@ -1453,7 +1453,7 @@ impl Interactivity { cx.on_mouse_event(move |_: &MouseUpEvent, phase, cx| { if phase == DispatchPhase::Capture { *active_state.borrow_mut() = ElementClickedState::default(); - cx.notify(); + cx.refresh(); } }); } else { @@ -1471,7 +1471,7 @@ impl Interactivity { if group || element { *active_state.borrow_mut() = ElementClickedState { group, element }; - cx.notify(); + cx.refresh(); } } }); @@ -1531,7 +1531,7 @@ impl Interactivity { } if *scroll_offset != old_scroll_offset { - cx.notify(); + cx.refresh(); cx.stop_propagation(); } } diff --git a/crates/gpui/src/elements/img.rs b/crates/gpui/src/elements/img.rs index 650b5b666bc821e15873c53f2cdc56c0546b6a20..123bfed42a38642b1789675ffa00764418169be9 100644 --- a/crates/gpui/src/elements/img.rs +++ b/crates/gpui/src/elements/img.rs @@ -109,7 +109,7 @@ impl Element for Img { } else { cx.spawn(|mut cx| async move { if image_future.await.ok().is_some() { - cx.on_next_frame(|cx| cx.notify()); + cx.on_next_frame(|cx| cx.refresh()); } }) .detach(); diff --git a/crates/gpui/src/elements/list.rs b/crates/gpui/src/elements/list.rs index 2a47a16741cf67c0cefb8a094d2f9e506cacbdf4..9081ceadca8440e6ac7d631b460d1b4b277ff932 100644 --- a/crates/gpui/src/elements/list.rs +++ b/crates/gpui/src/elements/list.rs @@ -258,7 +258,7 @@ impl StateInner { ); } - cx.notify(); + cx.refresh(); } fn logical_scroll_top(&self) -> ListOffset { diff --git a/crates/gpui/src/elements/text.rs b/crates/gpui/src/elements/text.rs index 29c93fd19e91518a96a5f663352b92519264aca9..ec74eb2987db9e4234f15af6b13c926226b61ca9 100644 --- a/crates/gpui/src/elements/text.rs +++ b/crates/gpui/src/elements/text.rs @@ -389,7 +389,7 @@ impl Element for InteractiveText { } mouse_down.take(); - cx.notify(); + cx.refresh(); } }); } else { @@ -399,7 +399,7 @@ impl Element for InteractiveText { text_state.index_for_position(bounds, event.position) { mouse_down.set(Some(mouse_down_index)); - cx.notify(); + cx.refresh(); } } }); diff --git a/crates/gpui/src/view.rs b/crates/gpui/src/view.rs index 6ec78e0d2e8bd62dc5e30d7a47ba77cd254cb892..22443a939546d9c86a534d62407ca18d090e3db4 100644 --- a/crates/gpui/src/view.rs +++ b/crates/gpui/src/view.rs @@ -290,6 +290,7 @@ impl Element for AnyView { && cache_key.stacking_order == *cx.stacking_order() && cache_key.text_style == cx.text_style() && !cx.window.dirty_views.contains(&self.entity_id()) + && !cx.window.refreshing { cx.reuse_geometry(); return; diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 376f4c24662f39a766c9aaa3ce3b2c69a4f75f9c..fc60f9c5c317ad81a16d64cfd03232781676ca22 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -269,6 +269,7 @@ pub struct Window { bounds_observers: SubscriberSet<(), AnyObserver>, active: bool, pub(crate) dirty: bool, + pub(crate) refreshing: bool, pub(crate) drawing: bool, activation_observers: SubscriberSet<(), AnyObserver>, pub(crate) focus: Option, @@ -424,6 +425,7 @@ impl Window { bounds_observers: SubscriberSet::new(), active: false, dirty: false, + refreshing: false, drawing: false, activation_observers: SubscriberSet::new(), focus: None, @@ -478,8 +480,9 @@ impl<'a> WindowContext<'a> { } /// Mark the window as dirty, scheduling it to be redrawn on the next frame. - pub fn notify(&mut self) { + pub fn refresh(&mut self) { if !self.window.drawing { + self.window.refreshing = true; self.window.dirty = true; } } @@ -519,7 +522,7 @@ impl<'a> WindowContext<'a> { self.window.focus_invalidated = true; } - self.notify(); + self.refresh(); } /// Remove focus from all elements within this context's window. @@ -529,7 +532,7 @@ impl<'a> WindowContext<'a> { } self.window.focus = None; - self.notify(); + self.refresh(); } pub fn disable_focus(&mut self) { @@ -795,7 +798,7 @@ impl<'a> WindowContext<'a> { self.window.viewport_size = self.window.platform_window.content_size(); self.window.bounds = self.window.platform_window.bounds(); self.window.display_id = self.window.platform_window.display().id(); - self.notify(); + self.refresh(); self.window .bounds_observers @@ -1499,6 +1502,7 @@ impl<'a> WindowContext<'a> { self.platform.set_cursor_style(cursor_style); } + self.window.refreshing = false; self.window.drawing = false; ELEMENT_ARENA.with_borrow_mut(|element_arena| element_arena.clear()); @@ -1641,12 +1645,12 @@ impl<'a> WindowContext<'a> { if event.is::() { // If this was a mouse move event, redraw the window so that the // active drag can follow the mouse cursor. - self.notify(); + self.refresh(); } else if event.is::() { // If this was a mouse up event, cancel the active drag and redraw // the window. self.active_drag = None; - self.notify(); + self.refresh(); } } } @@ -2169,7 +2173,7 @@ impl VisualContext for WindowContext<'_> { { let view = self.new_view(build_view); self.window.root_view = Some(view.clone().into()); - self.notify(); + self.refresh(); view } @@ -2583,7 +2587,7 @@ impl<'a, V: 'static> ViewContext<'a, V> { } if !self.window.drawing { - self.window_cx.notify(); + self.window_cx.window.dirty = true; self.window_cx.app.push_effect(Effect::Notify { emitter: self.view.model.entity_id, }); diff --git a/crates/terminal_view/src/terminal_element.rs b/crates/terminal_view/src/terminal_element.rs index d936716032a53b432d2f6f1a5dc6b79069656c8b..9cc55f0e7451698b3cfcc292cb0c8929ff2b489e 100644 --- a/crates/terminal_view/src/terminal_element.rs +++ b/crates/terminal_view/src/terminal_element.rs @@ -584,7 +584,7 @@ impl TerminalElement { this.update(cx, |term, _| term.try_modifiers_change(&event.modifiers)); if handled { - cx.notify(); + cx.refresh(); } } }); diff --git a/crates/ui/src/components/popover_menu.rs b/crates/ui/src/components/popover_menu.rs index fb823b05dba3d18cea7992948abb50f827167e1c..bc27e5eea2f1ee4e01db197395ab9aeccdf3fb27 100644 --- a/crates/ui/src/components/popover_menu.rs +++ b/crates/ui/src/components/popover_menu.rs @@ -55,7 +55,7 @@ impl PopoverMenu { } } *menu2.borrow_mut() = None; - cx.notify(); + cx.refresh(); }) .detach(); cx.focus_view(&new_menu); diff --git a/crates/ui/src/components/right_click_menu.rs b/crates/ui/src/components/right_click_menu.rs index 8bf40f61a8264ec3b00eec3ec844efa247876da7..56590cdde89f99a8f51f8bb4b10ddee7a99fc7bd 100644 --- a/crates/ui/src/components/right_click_menu.rs +++ b/crates/ui/src/components/right_click_menu.rs @@ -153,7 +153,7 @@ impl Element for RightClickMenu { } } *menu2.borrow_mut() = None; - cx.notify(); + cx.refresh(); }) .detach(); cx.focus_view(&new_menu); @@ -166,7 +166,7 @@ impl Element for RightClickMenu { } else { cx.mouse_position() }; - cx.notify(); + cx.refresh(); } }); } diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index 236daf60f8a747f4d43f4bc0a73109cd16e2ad3e..9ce191bc94644c3fa714d8b4c696aa0c7ae5f0e5 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -698,7 +698,7 @@ mod element { // todo!(schedule serialize) // workspace.schedule_serialize(cx); - cx.notify(); + cx.refresh(); } fn push_handle( From a4ef1bc096792e94610ea2bdb38fff1d887db71b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 10 Jan 2024 23:06:10 +0100 Subject: [PATCH 11/22] Rename reuse_geometry to reuse_view --- crates/gpui/src/scene.rs | 2 +- crates/gpui/src/view.rs | 2 +- crates/gpui/src/window.rs | 82 ++++++++++++++++----------------------- 3 files changed, 35 insertions(+), 51 deletions(-) diff --git a/crates/gpui/src/scene.rs b/crates/gpui/src/scene.rs index 9c63f803254b8f099be1cb7c369b41458a253df2..4b716413f50b8c0458023ce2cea926cb206376fa 100644 --- a/crates/gpui/src/scene.rs +++ b/crates/gpui/src/scene.rs @@ -125,7 +125,7 @@ impl Scene { } } - pub fn insert_views_from_scene(&mut self, views: &FxHashSet, prev_scene: &mut Self) { + pub fn reuse_views(&mut self, views: &FxHashSet, prev_scene: &mut Self) { for shadow in prev_scene.shadows.drain(..) { if views.contains(&EntityId::from(shadow.view_id as u64)) { let order = &prev_scene.orders_by_layer[&shadow.layer_id]; diff --git a/crates/gpui/src/view.rs b/crates/gpui/src/view.rs index 22443a939546d9c86a534d62407ca18d090e3db4..961c6aef64cde75b6ea1554a0dd6c16f1c8326af 100644 --- a/crates/gpui/src/view.rs +++ b/crates/gpui/src/view.rs @@ -292,7 +292,7 @@ impl Element for AnyView { && !cx.window.dirty_views.contains(&self.entity_id()) && !cx.window.refreshing { - cx.reuse_geometry(); + cx.reuse_view(); return; } } diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index be7391495174252a190bc69dadb6c7d1cc820d2a..2e84260b9e45aa89593e568296c623750189a3a8 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -347,6 +347,37 @@ impl Frame { .map(|focus_id| self.dispatch_tree.focus_path(focus_id)) .unwrap_or_default() } + + fn reuse_views(&mut self, prev_frame: &mut Self) { + // Reuse mouse listeners that didn't change since the last frame. + for (type_id, listeners) in &mut prev_frame.mouse_listeners { + let next_listeners = self.mouse_listeners.entry(*type_id).or_default(); + for (order, view_id, listener) in listeners.drain(..) { + if self.reused_views.contains(&view_id) { + next_listeners.push((order, view_id, listener)); + } + } + } + + // Reuse entries in the depth map that didn't change since the last frame. + for (order, view_id, bounds) in prev_frame.depth_map.drain(..) { + if self.reused_views.contains(&view_id) { + self.depth_map.push((order, view_id, bounds)); + } + } + self.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 prev_frame.element_states.drain() { + if self.reused_views.contains(&state.parent_view_id) { + self.element_states.entry(element_id).or_insert(state); + } + } + + // Reuse geometry that didn't change since the last frame. + self.scene + .reuse_views(&self.reused_views, &mut prev_frame.scene); + } } impl Window { @@ -1376,7 +1407,7 @@ impl<'a> WindowContext<'a> { ); } - pub(crate) fn reuse_geometry(&mut self) { + pub(crate) fn reuse_view(&mut self) { let view_id = self.parent_view_id().unwrap(); let window = &mut self.window; let grafted_view_ids = window @@ -1450,56 +1481,9 @@ impl<'a> WindowContext<'a> { self.window.next_frame.window_active = self.window.active; 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 - .next_frame - .mouse_listeners - .entry(*type_id) - .or_default(); - for (order, view_id, listener) in listeners.drain(..) { - if self.window.next_frame.reused_views.contains(&view_id) { - next_listeners.push((order, view_id, listener)); - } - } - } - - // 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, - ); + .reuse_views(&mut self.window.rendered_frame); self.window.next_frame.scene.finish(); let previous_focus_path = self.window.rendered_frame.focus_path(); From 142a8b68c856264773f9cb593a155de378db87df Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 11 Jan 2024 12:28:48 +0100 Subject: [PATCH 12/22] Avoid casting view ids to u32 Also, it looks like using a u64 directly doesn't work well with Metal shaders, so we unpack the u64 into two u32s. --- crates/editor/src/element.rs | 10 +++---- crates/gpui/src/scene.rs | 58 +++++++++++++++++++++++++----------- crates/gpui/src/window.rs | 18 +++++------ 3 files changed, 55 insertions(+), 31 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 7efb43bd4852fc7ef1d8e5a5a43a79ad72a0303e..53578392e8cf2c95c3415f5e5e3de950c5db4abc 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -26,11 +26,11 @@ use git::diff::DiffHunkStatus; use gpui::{ div, fill, outline, overlay, point, px, quad, relative, size, transparent_black, Action, AnchorCorner, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Corners, - CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Hsla, InteractiveBounds, - InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton, MouseDownEvent, - MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine, - SharedString, Size, StackingOrder, StatefulInteractiveElement, Style, Styled, TextRun, - TextStyle, View, ViewContext, WindowContext, + CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Entity, Hsla, + InteractiveBounds, InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton, + MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, ScrollDelta, + ScrollWheelEvent, ShapedLine, SharedString, Size, StackingOrder, StatefulInteractiveElement, + Style, Styled, TextRun, TextStyle, View, ViewContext, WindowContext, }; use itertools::Itertools; use language::language_settings::ShowWhitespaceSetting; diff --git a/crates/gpui/src/scene.rs b/crates/gpui/src/scene.rs index 4b716413f50b8c0458023ce2cea926cb206376fa..4179a1659e768c3967cbd18df030db0779535f79 100644 --- a/crates/gpui/src/scene.rs +++ b/crates/gpui/src/scene.rs @@ -13,6 +13,30 @@ pub(crate) type PathVertex_ScaledPixels = PathVertex; pub type LayerId = u32; pub type DrawOrder = u32; +#[derive(Default, Copy, Clone, Debug, Eq, PartialEq, Hash)] +#[repr(C)] +pub struct ViewId { + low_bits: u32, + high_bits: u32, +} + +impl From for ViewId { + fn from(value: EntityId) -> Self { + let value = value.as_u64(); + Self { + low_bits: value as u32, + high_bits: (value >> 32) as u32, + } + } +} + +impl From for EntityId { + fn from(value: ViewId) -> Self { + let value = (value.low_bits as u64) | ((value.high_bits as u64) << 32); + value.into() + } +} + #[derive(Default)] pub struct Scene { layers_by_order: BTreeMap, @@ -127,49 +151,49 @@ impl Scene { pub fn reuse_views(&mut self, views: &FxHashSet, prev_scene: &mut Self) { for shadow in prev_scene.shadows.drain(..) { - if views.contains(&EntityId::from(shadow.view_id as u64)) { + if views.contains(&shadow.view_id.into()) { let order = &prev_scene.orders_by_layer[&shadow.layer_id]; self.insert(&order, shadow); } } for quad in prev_scene.quads.drain(..) { - if views.contains(&EntityId::from(quad.view_id as u64)) { + if views.contains(&quad.view_id.into()) { let order = &prev_scene.orders_by_layer[&quad.layer_id]; self.insert(&order, quad); } } for path in prev_scene.paths.drain(..) { - if views.contains(&EntityId::from(path.view_id as u64)) { + if views.contains(&path.view_id.into()) { let order = &prev_scene.orders_by_layer[&path.layer_id]; self.insert(&order, path); } } for underline in prev_scene.underlines.drain(..) { - if views.contains(&EntityId::from(underline.view_id as u64)) { + if views.contains(&underline.view_id.into()) { let order = &prev_scene.orders_by_layer[&underline.layer_id]; self.insert(&order, underline); } } for sprite in prev_scene.monochrome_sprites.drain(..) { - if views.contains(&EntityId::from(sprite.view_id as u64)) { + if views.contains(&sprite.view_id.into()) { let order = &prev_scene.orders_by_layer[&sprite.layer_id]; self.insert(&order, sprite); } } for sprite in prev_scene.polychrome_sprites.drain(..) { - if views.contains(&EntityId::from(sprite.view_id as u64)) { + if views.contains(&sprite.view_id.into()) { let order = &prev_scene.orders_by_layer[&sprite.layer_id]; self.insert(&order, sprite); } } for surface in prev_scene.surfaces.drain(..) { - if views.contains(&EntityId::from(surface.view_id as u64)) { + if views.contains(&surface.view_id.into()) { let order = &prev_scene.orders_by_layer[&surface.layer_id]; self.insert(&order, surface); } @@ -470,7 +494,7 @@ pub(crate) enum PrimitiveBatch<'a> { #[derive(Default, Debug, Clone, Eq, PartialEq)] #[repr(C)] pub struct Quad { - pub view_id: u32, + pub view_id: ViewId, pub layer_id: LayerId, pub order: DrawOrder, pub bounds: Bounds, @@ -502,7 +526,7 @@ impl From for Primitive { #[derive(Debug, Clone, Eq, PartialEq)] #[repr(C)] pub struct Underline { - pub view_id: u32, + pub view_id: ViewId, pub layer_id: LayerId, pub order: DrawOrder, pub bounds: Bounds, @@ -533,7 +557,7 @@ impl From for Primitive { #[derive(Debug, Clone, Eq, PartialEq)] #[repr(C)] pub struct Shadow { - pub view_id: u32, + pub view_id: ViewId, pub layer_id: LayerId, pub order: DrawOrder, pub bounds: Bounds, @@ -564,7 +588,7 @@ impl From for Primitive { #[derive(Clone, Debug, Eq, PartialEq)] #[repr(C)] pub struct MonochromeSprite { - pub view_id: u32, + pub view_id: ViewId, pub layer_id: LayerId, pub order: DrawOrder, pub bounds: Bounds, @@ -597,7 +621,7 @@ impl From for Primitive { #[derive(Clone, Debug, Eq, PartialEq)] #[repr(C)] pub struct PolychromeSprite { - pub view_id: u32, + pub view_id: ViewId, pub layer_id: LayerId, pub order: DrawOrder, pub bounds: Bounds, @@ -630,7 +654,7 @@ impl From for Primitive { #[derive(Clone, Debug, Eq, PartialEq)] pub struct Surface { - pub view_id: u32, + pub view_id: ViewId, pub layer_id: LayerId, pub order: DrawOrder, pub bounds: Bounds, @@ -662,7 +686,7 @@ pub(crate) struct PathId(pub(crate) usize); #[derive(Debug)] pub struct Path { pub(crate) id: PathId, - pub(crate) view_id: u32, + pub(crate) view_id: ViewId, layer_id: LayerId, order: DrawOrder, pub(crate) bounds: Bounds

, @@ -678,9 +702,9 @@ impl Path { pub fn new(start: Point) -> Self { Self { id: PathId(0), - view_id: 0, - layer_id: 0, - order: 0, + view_id: ViewId::default(), + layer_id: LayerId::default(), + order: DrawOrder::default(), vertices: Vec::new(), start, current: start, diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 2e84260b9e45aa89593e568296c623750189a3a8..34df4b24236be52108e68771ded67140e4a43b0c 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -1102,7 +1102,7 @@ impl<'a> WindowContext<'a> { window.next_frame.scene.insert( &window.next_frame.z_index_stack, Shadow { - view_id: view_id.as_u64() as u32, + view_id: view_id.into(), layer_id: 0, order: 0, bounds: shadow_bounds.scale(scale_factor), @@ -1127,7 +1127,7 @@ impl<'a> WindowContext<'a> { window.next_frame.scene.insert( &window.next_frame.z_index_stack, Quad { - view_id: view_id.as_u64() as u32, + view_id: view_id.into(), layer_id: 0, order: 0, bounds: quad.bounds.scale(scale_factor), @@ -1148,7 +1148,7 @@ impl<'a> WindowContext<'a> { path.content_mask = content_mask; path.color = color.into(); - path.view_id = view_id.as_u64() as u32; + path.view_id = view_id.into(); let window = &mut *self.window; window .next_frame @@ -1180,7 +1180,7 @@ impl<'a> WindowContext<'a> { window.next_frame.scene.insert( &window.next_frame.z_index_stack, Underline { - view_id: view_id.as_u64() as u32, + view_id: view_id.into(), layer_id: 0, order: 0, bounds: bounds.scale(scale_factor), @@ -1236,7 +1236,7 @@ impl<'a> WindowContext<'a> { window.next_frame.scene.insert( &window.next_frame.z_index_stack, MonochromeSprite { - view_id: view_id.as_u64() as u32, + view_id: view_id.into(), layer_id: 0, order: 0, bounds, @@ -1290,7 +1290,7 @@ impl<'a> WindowContext<'a> { window.next_frame.scene.insert( &window.next_frame.z_index_stack, PolychromeSprite { - view_id: view_id.as_u64() as u32, + view_id: view_id.into(), layer_id: 0, order: 0, bounds, @@ -1335,7 +1335,7 @@ impl<'a> WindowContext<'a> { window.next_frame.scene.insert( &window.next_frame.z_index_stack, MonochromeSprite { - view_id: view_id.as_u64() as u32, + view_id: view_id.into(), layer_id: 0, order: 0, bounds, @@ -1374,7 +1374,7 @@ impl<'a> WindowContext<'a> { window.next_frame.scene.insert( &window.next_frame.z_index_stack, PolychromeSprite { - view_id: view_id.as_u64() as u32, + view_id: view_id.into(), layer_id: 0, order: 0, bounds, @@ -1397,7 +1397,7 @@ impl<'a> WindowContext<'a> { window.next_frame.scene.insert( &window.next_frame.z_index_stack, Surface { - view_id: view_id.as_u64() as u32, + view_id: view_id.into(), layer_id: 0, order: 0, bounds, From d088ace4047e6a97a35b7372fc18dc0ac1823668 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 11 Jan 2024 12:48:05 +0100 Subject: [PATCH 13/22] Explicitly push a node in the dispatch tree when painting a new view --- crates/editor/src/element.rs | 10 +++++----- crates/gpui/src/key_dispatch.rs | 9 --------- crates/gpui/src/view.rs | 6 +++--- crates/gpui/src/window.rs | 15 +++++++++++++-- 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 53578392e8cf2c95c3415f5e5e3de950c5db4abc..7efb43bd4852fc7ef1d8e5a5a43a79ad72a0303e 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -26,11 +26,11 @@ use git::diff::DiffHunkStatus; use gpui::{ div, fill, outline, overlay, point, px, quad, relative, size, transparent_black, Action, AnchorCorner, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Corners, - CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Entity, Hsla, - InteractiveBounds, InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton, - MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, ScrollDelta, - ScrollWheelEvent, ShapedLine, SharedString, Size, StackingOrder, StatefulInteractiveElement, - Style, Styled, TextRun, TextStyle, View, ViewContext, WindowContext, + CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Hsla, InteractiveBounds, + InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton, MouseDownEvent, + MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine, + SharedString, Size, StackingOrder, StatefulInteractiveElement, Style, Styled, TextRun, + TextStyle, View, ViewContext, WindowContext, }; use itertools::Itertools; use language::language_settings::ShowWhitespaceSetting; diff --git a/crates/gpui/src/key_dispatch.rs b/crates/gpui/src/key_dispatch.rs index b3fdddf00f0c20fcfeda0befab58bb0b485501bc..06d502d7780c9c59dc180d3508d7e4074009dbb6 100644 --- a/crates/gpui/src/key_dispatch.rs +++ b/crates/gpui/src/key_dispatch.rs @@ -73,15 +73,6 @@ 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 { diff --git a/crates/gpui/src/view.rs b/crates/gpui/src/view.rs index 961c6aef64cde75b6ea1554a0dd6c16f1c8326af..968fbbd94cd142bdfc6629539da997652f71d91c 100644 --- a/crates/gpui/src/view.rs +++ b/crates/gpui/src/view.rs @@ -97,7 +97,7 @@ impl Element for View { } fn paint(&mut self, _: Bounds, element: &mut Self::State, cx: &mut WindowContext) { - cx.with_view_id(self.entity_id(), |cx| element.take().unwrap().paint(cx)); + cx.paint_view(self.entity_id(), |cx| element.take().unwrap().paint(cx)); } } @@ -230,7 +230,7 @@ impl AnyView { available_space: Size, cx: &mut WindowContext, ) { - cx.with_view_id(self.entity_id(), |cx| { + cx.paint_view(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); @@ -278,7 +278,7 @@ impl Element for AnyView { } fn paint(&mut self, bounds: Bounds, state: &mut Self::State, cx: &mut WindowContext) { - cx.with_view_id(self.entity_id(), |cx| { + cx.paint_view(self.entity_id(), |cx| { if !self.cache { state.element.take().unwrap().paint(cx); return; diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 34df4b24236be52108e68771ded67140e4a43b0c..3bdd8607c031008e161b64d607a6b41ff870f702 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -1942,13 +1942,12 @@ 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, parent_view_id); + .push_node(context.clone(), focus_id, None); let result = f(focus_handle, self); @@ -1968,6 +1967,18 @@ impl<'a> WindowContext<'a> { result } + pub(crate) fn paint_view(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R { + self.with_view_id(view_id, |cx| { + cx.window + .next_frame + .dispatch_tree + .push_node(None, None, Some(view_id)); + let result = f(cx); + cx.window.next_frame.dispatch_tree.pop_node(); + 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 From 50ccdf5c167897baf39a929f8ec714d053733631 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 11 Jan 2024 13:22:59 +0100 Subject: [PATCH 14/22] Reuse input handler when reusing a view tree --- crates/gpui/src/platform.rs | 2 +- crates/gpui/src/platform/mac/window.rs | 4 +- crates/gpui/src/platform/test/window.rs | 4 +- crates/gpui/src/window.rs | 86 ++++++++++++++++++------- 4 files changed, 67 insertions(+), 29 deletions(-) diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index c41bf998f698443d5b6ca93dee2966206596d533..5a2335919ebe6ff9b2277e02d4f6f55b4dc9a80c 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -145,7 +145,7 @@ pub trait PlatformWindow { fn modifiers(&self) -> Modifiers; fn as_any_mut(&mut self) -> &mut dyn Any; fn set_input_handler(&mut self, input_handler: Box); - fn clear_input_handler(&mut self); + fn take_input_handler(&mut self) -> Option>; fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver; fn activate(&self); fn set_title(&mut self, title: &str); diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index 7c96fc0198895ce426f483c2c03ce0c888df8d3c..534c43284078697333c6a7e95186160b13302ad6 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -770,8 +770,8 @@ impl PlatformWindow for MacWindow { self.0.as_ref().lock().input_handler = Some(input_handler); } - fn clear_input_handler(&mut self) { - self.0.as_ref().lock().input_handler = None; + fn take_input_handler(&mut self) -> Option> { + self.0.as_ref().lock().input_handler.take() } fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver { diff --git a/crates/gpui/src/platform/test/window.rs b/crates/gpui/src/platform/test/window.rs index 029fd7300998f55f089696d6e01199b9c687ddfd..f05e13e3a027e2be9d4f17690fe7162a73396d03 100644 --- a/crates/gpui/src/platform/test/window.rs +++ b/crates/gpui/src/platform/test/window.rs @@ -167,8 +167,8 @@ impl PlatformWindow for TestWindow { self.0.lock().input_handler = Some(input_handler); } - fn clear_input_handler(&mut self) { - self.0.lock().input_handler = None; + fn take_input_handler(&mut self) -> Option> { + self.0.lock().input_handler.take() } fn prompt( diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 3bdd8607c031008e161b64d607a6b41ff870f702..65f6aa42fffdaab131f4f9c96e65abb756ad5071 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -296,6 +296,11 @@ pub(crate) struct ElementStateBox { type_name: &'static str, } +struct RequestedInputHandler { + view_id: EntityId, + handler: Option>, +} + pub(crate) struct Frame { focus: Option, window_active: bool, @@ -308,6 +313,7 @@ pub(crate) struct Frame { pub(crate) next_stacking_order_id: u32, content_mask_stack: Vec>, element_offset_stack: Vec>, + requested_input_handler: Option, pub(crate) view_stack: Vec, pub(crate) reused_views: FxHashSet, } @@ -323,9 +329,10 @@ impl Frame { scene: Scene::default(), z_index_stack: StackingOrder::default(), next_stacking_order_id: 0, - depth_map: Default::default(), + depth_map: Vec::new(), content_mask_stack: Vec::new(), element_offset_stack: Vec::new(), + requested_input_handler: None, view_stack: Vec::new(), reused_views: FxHashSet::default(), } @@ -947,7 +954,7 @@ impl<'a> WindowContext<'a> { &mut self, mut handler: impl FnMut(&Event, DispatchPhase, &mut WindowContext) + 'static, ) { - let view_id = self.parent_view_id().unwrap(); + let view_id = self.parent_view_id(); let order = self.window.next_frame.z_index_stack.clone(); self.window .next_frame @@ -1036,7 +1043,7 @@ 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 view_id = self.parent_view_id().unwrap(); + let view_id = self.parent_view_id(); self.window .next_frame .depth_map @@ -1093,7 +1100,7 @@ impl<'a> WindowContext<'a> { ) { let scale_factor = self.scale_factor(); let content_mask = self.content_mask(); - let view_id = self.parent_view_id().unwrap(); + let view_id = self.parent_view_id(); let window = &mut *self.window; for shadow in shadows { let mut shadow_bounds = bounds; @@ -1121,7 +1128,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.parent_view_id().unwrap(); + let view_id = self.parent_view_id(); let window = &mut *self.window; window.next_frame.scene.insert( @@ -1144,7 +1151,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.parent_view_id().unwrap(); + let view_id = self.parent_view_id(); path.content_mask = content_mask; path.color = color.into(); @@ -1174,7 +1181,7 @@ impl<'a> WindowContext<'a> { size: size(width, height), }; let content_mask = self.content_mask(); - let view_id = self.parent_view_id().unwrap(); + let view_id = self.parent_view_id(); let window = &mut *self.window; window.next_frame.scene.insert( @@ -1231,7 +1238,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.parent_view_id().unwrap(); + let view_id = self.parent_view_id(); let window = &mut *self.window; window.next_frame.scene.insert( &window.next_frame.z_index_stack, @@ -1284,7 +1291,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.parent_view_id().unwrap(); + let view_id = self.parent_view_id(); let window = &mut *self.window; window.next_frame.scene.insert( @@ -1329,7 +1336,7 @@ impl<'a> WindowContext<'a> { Ok((params.size, Cow::Owned(bytes))) })?; let content_mask = self.content_mask().scale(scale_factor); - let view_id = self.parent_view_id().unwrap(); + let view_id = self.parent_view_id(); let window = &mut *self.window; window.next_frame.scene.insert( @@ -1368,7 +1375,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.parent_view_id().unwrap(); + let view_id = self.parent_view_id(); let window = &mut *self.window; window.next_frame.scene.insert( @@ -1392,7 +1399,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.parent_view_id().unwrap(); + let view_id = self.parent_view_id(); let window = &mut *self.window; window.next_frame.scene.insert( &window.next_frame.z_index_stack, @@ -1408,14 +1415,27 @@ impl<'a> WindowContext<'a> { } pub(crate) fn reuse_view(&mut self) { - let view_id = self.parent_view_id().unwrap(); - let window = &mut self.window; - let grafted_view_ids = window + let view_id = self.parent_view_id(); + let grafted_view_ids = self + .window .next_frame .dispatch_tree - .graft(view_id, &mut window.rendered_frame.dispatch_tree); + .graft(view_id, &mut self.window.rendered_frame.dispatch_tree); for view_id in grafted_view_ids { - assert!(window.next_frame.reused_views.insert(view_id)); + assert!(self.window.next_frame.reused_views.insert(view_id)); + + // Reuse the previous input handler if it was associated with one of + // the views grafted from the tree in the previous frame. + if self + .window + .rendered_frame + .requested_input_handler + .as_ref() + .map_or(false, |requested| requested.view_id == view_id) + { + self.window.next_frame.requested_input_handler = + self.window.rendered_frame.requested_input_handler.take(); + } } } @@ -1430,7 +1450,11 @@ impl<'a> WindowContext<'a> { } self.text_system().start_frame(); - self.window.platform_window.clear_input_handler(); + if let Some(requested_handler) = self.window.rendered_frame.requested_input_handler.as_mut() + { + requested_handler.handler = self.window.platform_window.take_input_handler(); + } + self.window.layout_engine.as_mut().unwrap().clear(); self.window.next_frame.clear(); let root_view = self.window.root_view.take().unwrap(); @@ -1486,6 +1510,13 @@ impl<'a> WindowContext<'a> { .reuse_views(&mut self.window.rendered_frame); self.window.next_frame.scene.finish(); + // Register requested input handler with the platform window. + if let Some(requested_input) = self.window.next_frame.requested_input_handler.as_mut() { + if let Some(handler) = requested_input.handler.take() { + self.window.platform_window.set_input_handler(handler); + } + } + let previous_focus_path = self.window.rendered_frame.focus_path(); let previous_window_active = self.window.rendered_frame.window_active; mem::swap(&mut self.window.rendered_frame, &mut self.window.next_frame); @@ -2054,7 +2085,7 @@ impl<'a> WindowContext<'a> { result } else { let (result, state) = f(None, cx); - let parent_view_id = cx.parent_view_id().unwrap(); + let parent_view_id = cx.parent_view_id(); cx.window_mut() .next_frame .element_states @@ -2072,8 +2103,13 @@ impl<'a> WindowContext<'a> { }) } - fn parent_view_id(&self) -> Option { - self.window.next_frame.view_stack.last().copied() + fn parent_view_id(&self) -> EntityId { + *self + .window + .next_frame + .view_stack + .last() + .expect("a view should always be on the stack while drawing") } /// Set an input handler, such as [`ElementInputHandler`][element_input_handler], which interfaces with the @@ -2087,9 +2123,11 @@ impl<'a> WindowContext<'a> { input_handler: impl PlatformInputHandler, ) { if focus_handle.is_focused(self) { - self.window - .platform_window - .set_input_handler(Box::new(input_handler)); + let view_id = self.parent_view_id(); + self.window.next_frame.requested_input_handler = Some(RequestedInputHandler { + view_id, + handler: Some(Box::new(input_handler)), + }) } } From 18eaefd0ed31b90d5342c58e853365be61dfe0d0 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 11 Jan 2024 15:03:34 +0100 Subject: [PATCH 15/22] Reuse cursor style when reusing a view tree --- crates/gpui/src/window.rs | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 65f6aa42fffdaab131f4f9c96e65abb756ad5071..7e38353e477d4002d4ee3dde58f1f86be3dba21e 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -273,7 +273,6 @@ pub struct Window { default_prevented: bool, mouse_position: Point, modifiers: Modifiers, - requested_cursor_style: Option, scale_factor: f32, bounds: WindowBounds, bounds_observers: SubscriberSet<(), AnyObserver>, @@ -314,6 +313,8 @@ pub(crate) struct Frame { content_mask_stack: Vec>, element_offset_stack: Vec>, requested_input_handler: Option, + cursor_styles: FxHashMap, + requested_cursor_style: Option, pub(crate) view_stack: Vec, pub(crate) reused_views: FxHashSet, } @@ -333,6 +334,8 @@ impl Frame { content_mask_stack: Vec::new(), element_offset_stack: Vec::new(), requested_input_handler: None, + cursor_styles: FxHashMap::default(), + requested_cursor_style: None, view_stack: Vec::new(), reused_views: FxHashSet::default(), } @@ -346,6 +349,9 @@ impl Frame { self.next_stacking_order_id = 0; self.reused_views.clear(); self.scene.clear(); + self.requested_input_handler.take(); + self.cursor_styles.clear(); + self.requested_cursor_style.take(); debug_assert_eq!(self.view_stack.len(), 0); } @@ -469,7 +475,6 @@ impl Window { default_prevented: true, mouse_position, modifiers, - requested_cursor_style: None, scale_factor, bounds, bounds_observers: SubscriberSet::new(), @@ -1037,7 +1042,9 @@ impl<'a> WindowContext<'a> { /// Update the cursor style at the platform level. pub fn set_cursor_style(&mut self, style: CursorStyle) { - self.window.requested_cursor_style = Some(style) + let view_id = self.parent_view_id(); + self.window.next_frame.cursor_styles.insert(view_id, style); + self.window.next_frame.requested_cursor_style = Some(style); } /// Called during painting to track which z-index is on top at each pixel position @@ -1436,6 +1443,11 @@ impl<'a> WindowContext<'a> { self.window.next_frame.requested_input_handler = self.window.rendered_frame.requested_input_handler.take(); } + + if let Some(style) = self.window.rendered_frame.cursor_styles.remove(&view_id) { + self.window.next_frame.cursor_styles.insert(view_id, style); + self.window.next_frame.requested_cursor_style = Some(style); + } } } @@ -1505,6 +1517,17 @@ impl<'a> WindowContext<'a> { self.window.next_frame.window_active = self.window.active; self.window.root_view = Some(root_view); + // Set the cursor only if we're the active window. + let cursor_style = self + .window + .next_frame + .requested_cursor_style + .take() + .unwrap_or(CursorStyle::Arrow); + if self.is_window_active() { + self.platform.set_cursor_style(cursor_style); + } + self.window .next_frame .reuse_views(&mut self.window.rendered_frame); @@ -1523,16 +1546,6 @@ impl<'a> WindowContext<'a> { let current_focus_path = self.window.rendered_frame.focus_path(); let current_window_active = self.window.rendered_frame.window_active; - // Set the cursor only if we're the active window. - let cursor_style = self - .window - .requested_cursor_style - .take() - .unwrap_or(CursorStyle::Arrow); - if self.is_window_active() { - self.platform.set_cursor_style(cursor_style); - } - self.window.refreshing = false; self.window.drawing = false; ELEMENT_ARENA.with_borrow_mut(|element_arena| element_arena.clear()); From cbbba41748092724bcf3d3eefe1667894d958ba8 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 11 Jan 2024 16:57:06 +0100 Subject: [PATCH 16/22] Reuse line layouts when reusing view --- crates/gpui/src/text_system.rs | 16 +++++-- crates/gpui/src/text_system/line_layout.rs | 51 +++++++++++++++++++--- crates/gpui/src/window.rs | 36 ++++++++------- 3 files changed, 78 insertions(+), 25 deletions(-) diff --git a/crates/gpui/src/text_system.rs b/crates/gpui/src/text_system.rs index 47073bcde0ef77b7b6674d0c0a24b7dfa107e948..093ef3a7d5d0537975381ea1086eea68bdaf47bd 100644 --- a/crates/gpui/src/text_system.rs +++ b/crates/gpui/src/text_system.rs @@ -9,11 +9,11 @@ pub use line_layout::*; pub use line_wrapper::*; use crate::{ - px, Bounds, DevicePixels, Hsla, Pixels, PlatformTextSystem, Point, Result, SharedString, Size, - UnderlineStyle, + px, Bounds, DevicePixels, EntityId, Hsla, Pixels, PlatformTextSystem, Point, Result, + SharedString, Size, UnderlineStyle, }; use anyhow::anyhow; -use collections::FxHashMap; +use collections::{FxHashMap, FxHashSet}; use core::fmt; use itertools::Itertools; use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard}; @@ -186,6 +186,10 @@ impl TextSystem { } } + pub fn with_view(&self, view_id: EntityId, f: impl FnOnce() -> R) -> R { + self.line_layout_cache.with_view(view_id, f) + } + pub fn layout_line( &self, text: &str, @@ -361,7 +365,11 @@ impl TextSystem { } pub fn start_frame(&self) { - self.line_layout_cache.start_frame() + self.line_layout_cache.start_frame(); + } + + pub fn end_frame(&self, reused_views: &FxHashSet) { + self.line_layout_cache.end_frame(reused_views) } pub fn line_wrapper( diff --git a/crates/gpui/src/text_system/line_layout.rs b/crates/gpui/src/text_system/line_layout.rs index 6506d7794c7b4693e61af6d278c06a191528f448..909f0513467a9f3f173e2edfeb489472fcd7afc8 100644 --- a/crates/gpui/src/text_system/line_layout.rs +++ b/crates/gpui/src/text_system/line_layout.rs @@ -1,5 +1,5 @@ -use crate::{px, FontId, GlyphId, Pixels, PlatformTextSystem, Point, Size}; -use collections::FxHashMap; +use crate::{px, EntityId, FontId, GlyphId, Pixels, PlatformTextSystem, Point, Size}; +use collections::{FxHashMap, FxHashSet}; use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard}; use smallvec::SmallVec; use std::{ @@ -236,6 +236,7 @@ impl WrappedLineLayout { } pub(crate) struct LineLayoutCache { + view_stack: Mutex>, previous_frame: Mutex>>, current_frame: RwLock>>, previous_frame_wrapped: Mutex>>, @@ -246,6 +247,7 @@ pub(crate) struct LineLayoutCache { impl LineLayoutCache { pub fn new(platform_text_system: Arc) -> Self { Self { + view_stack: Mutex::default(), previous_frame: Mutex::default(), current_frame: RwLock::default(), previous_frame_wrapped: Mutex::default(), @@ -254,11 +256,43 @@ impl LineLayoutCache { } } - pub fn start_frame(&self) { + pub fn end_frame(&self, reused_views: &FxHashSet) { + debug_assert_eq!(self.view_stack.lock().len(), 0); + let mut prev_frame = self.previous_frame.lock(); let mut curr_frame = self.current_frame.write(); + for (key, layout) in prev_frame.drain() { + if key + .parent_view_id + .map_or(false, |view_id| reused_views.contains(&view_id)) + { + curr_frame.insert(key, layout); + } + } std::mem::swap(&mut *prev_frame, &mut *curr_frame); - curr_frame.clear(); + + let mut prev_frame_wrapped = self.previous_frame_wrapped.lock(); + let mut curr_frame_wrapped = self.current_frame_wrapped.write(); + for (key, layout) in prev_frame_wrapped.drain() { + if key + .parent_view_id + .map_or(false, |view_id| reused_views.contains(&view_id)) + { + curr_frame_wrapped.insert(key, layout); + } + } + std::mem::swap(&mut *prev_frame_wrapped, &mut *curr_frame_wrapped); + } + + pub fn with_view(&self, view_id: EntityId, f: impl FnOnce() -> R) -> R { + self.view_stack.lock().push(view_id); + let result = f(); + self.view_stack.lock().pop(); + result + } + + fn parent_view_id(&self) -> Option { + self.view_stack.lock().last().copied() } pub fn layout_wrapped_line( @@ -273,6 +307,7 @@ impl LineLayoutCache { font_size, runs, wrap_width, + parent_view_id: self.parent_view_id(), } as &dyn AsCacheKeyRef; let current_frame = self.current_frame_wrapped.upgradable_read(); @@ -301,6 +336,7 @@ impl LineLayoutCache { font_size, runs: SmallVec::from(runs), wrap_width, + parent_view_id: self.parent_view_id(), }; current_frame.insert(key, layout.clone()); layout @@ -313,6 +349,7 @@ impl LineLayoutCache { font_size, runs, wrap_width: None, + parent_view_id: self.parent_view_id(), } as &dyn AsCacheKeyRef; let current_frame = self.current_frame.upgradable_read(); @@ -331,6 +368,7 @@ impl LineLayoutCache { font_size, runs: SmallVec::from(runs), wrap_width: None, + parent_view_id: self.parent_view_id(), }; current_frame.insert(key, layout.clone()); layout @@ -348,12 +386,13 @@ trait AsCacheKeyRef { fn as_cache_key_ref(&self) -> CacheKeyRef; } -#[derive(Eq)] +#[derive(Debug, Eq)] struct CacheKey { text: String, font_size: Pixels, runs: SmallVec<[FontRun; 1]>, wrap_width: Option, + parent_view_id: Option, } #[derive(Copy, Clone, PartialEq, Eq, Hash)] @@ -362,6 +401,7 @@ struct CacheKeyRef<'a> { font_size: Pixels, runs: &'a [FontRun], wrap_width: Option, + parent_view_id: Option, } impl<'a> PartialEq for (dyn AsCacheKeyRef + 'a) { @@ -385,6 +425,7 @@ impl AsCacheKeyRef for CacheKey { font_size: self.font_size, runs: self.runs.as_slice(), wrap_width: self.wrap_width, + parent_view_id: self.parent_view_id, } } } diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 7e38353e477d4002d4ee3dde58f1f86be3dba21e..00d42144c73755a7c2f6557a97d77adbbf7a67a3 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -1461,14 +1461,11 @@ impl<'a> WindowContext<'a> { self.window.focus_invalidated = false; } - self.text_system().start_frame(); if let Some(requested_handler) = self.window.rendered_frame.requested_input_handler.as_mut() { requested_handler.handler = self.window.platform_window.take_input_handler(); } - self.window.layout_engine.as_mut().unwrap().clear(); - self.window.next_frame.clear(); let root_view = self.window.root_view.take().unwrap(); self.with_z_index(0, |cx| { @@ -1528,11 +1525,6 @@ impl<'a> WindowContext<'a> { self.platform.set_cursor_style(cursor_style); } - self.window - .next_frame - .reuse_views(&mut self.window.rendered_frame); - self.window.next_frame.scene.finish(); - // Register requested input handler with the platform window. if let Some(requested_input) = self.window.next_frame.requested_input_handler.as_mut() { if let Some(handler) = requested_input.handler.take() { @@ -1540,16 +1532,25 @@ impl<'a> WindowContext<'a> { } } + self.window + .next_frame + .reuse_views(&mut self.window.rendered_frame); + self.window.next_frame.scene.finish(); + self.window.layout_engine.as_mut().unwrap().clear(); + self.text_system() + .end_frame(&self.window.next_frame.reused_views); + ELEMENT_ARENA.with_borrow_mut(|element_arena| element_arena.clear()); + + self.window.refreshing = false; + self.window.drawing = false; + let previous_focus_path = self.window.rendered_frame.focus_path(); let previous_window_active = self.window.rendered_frame.window_active; mem::swap(&mut self.window.rendered_frame, &mut self.window.next_frame); + self.window.next_frame.clear(); let current_focus_path = self.window.rendered_frame.focus_path(); let current_window_active = self.window.rendered_frame.window_active; - self.window.refreshing = false; - self.window.drawing = false; - ELEMENT_ARENA.with_borrow_mut(|element_arena| element_arena.clear()); - if previous_focus_path != current_focus_path || previous_window_active != current_window_active { @@ -2005,10 +2006,13 @@ impl<'a> WindowContext<'a> { 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 + let text_system = self.text_system().clone(); + text_system.with_view(view_id, || { + self.window.next_frame.view_stack.push(view_id); + let result = f(self); + self.window.next_frame.view_stack.pop(); + result + }) } pub(crate) fn paint_view(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R { From 101cedb5f7e1cbe86e8340a428870081c59c39a5 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 11 Jan 2024 16:59:33 +0100 Subject: [PATCH 17/22] :lipstick: --- crates/gpui/src/text_system.rs | 8 ++------ crates/gpui/src/text_system/line_layout.rs | 2 +- crates/gpui/src/window.rs | 12 ++++++------ 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/crates/gpui/src/text_system.rs b/crates/gpui/src/text_system.rs index 093ef3a7d5d0537975381ea1086eea68bdaf47bd..7c5adef12d7de60c3a87c7280a41d9fd4c5df5bf 100644 --- a/crates/gpui/src/text_system.rs +++ b/crates/gpui/src/text_system.rs @@ -364,12 +364,8 @@ impl TextSystem { Ok(lines) } - pub fn start_frame(&self) { - self.line_layout_cache.start_frame(); - } - - pub fn end_frame(&self, reused_views: &FxHashSet) { - self.line_layout_cache.end_frame(reused_views) + pub fn finish_frame(&self, reused_views: &FxHashSet) { + self.line_layout_cache.finish_frame(reused_views) } pub fn line_wrapper( diff --git a/crates/gpui/src/text_system/line_layout.rs b/crates/gpui/src/text_system/line_layout.rs index 909f0513467a9f3f173e2edfeb489472fcd7afc8..6c466f9680e8ec8ff75812bcdf15a172c9ff6258 100644 --- a/crates/gpui/src/text_system/line_layout.rs +++ b/crates/gpui/src/text_system/line_layout.rs @@ -256,7 +256,7 @@ impl LineLayoutCache { } } - pub fn end_frame(&self, reused_views: &FxHashSet) { + pub fn finish_frame(&self, reused_views: &FxHashSet) { debug_assert_eq!(self.view_stack.lock().len(), 0); let mut prev_frame = self.previous_frame.lock(); diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 00d42144c73755a7c2f6557a97d77adbbf7a67a3..02683d655da04b57210a3bef6f99f6c85ab75873 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -361,7 +361,7 @@ impl Frame { .unwrap_or_default() } - fn reuse_views(&mut self, prev_frame: &mut Self) { + fn finish(&mut self, prev_frame: &mut Self) { // Reuse mouse listeners that didn't change since the last frame. for (type_id, listeners) in &mut prev_frame.mouse_listeners { let next_listeners = self.mouse_listeners.entry(*type_id).or_default(); @@ -390,6 +390,7 @@ impl Frame { // Reuse geometry that didn't change since the last frame. self.scene .reuse_views(&self.reused_views, &mut prev_frame.scene); + self.scene.finish(); } } @@ -1532,13 +1533,12 @@ impl<'a> WindowContext<'a> { } } - self.window - .next_frame - .reuse_views(&mut self.window.rendered_frame); - self.window.next_frame.scene.finish(); self.window.layout_engine.as_mut().unwrap().clear(); self.text_system() - .end_frame(&self.window.next_frame.reused_views); + .finish_frame(&self.window.next_frame.reused_views); + self.window + .next_frame + .finish(&mut self.window.rendered_frame); ELEMENT_ARENA.with_borrow_mut(|element_arena| element_arena.clear()); self.window.refreshing = false; From 11b433dc1cfee216796f36766f3b7ba492998982 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 11 Jan 2024 18:24:07 +0100 Subject: [PATCH 18/22] Move back to sorting entries in the depth map as we insert them --- crates/gpui/src/window.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 02683d655da04b57210a3bef6f99f6c85ab75873..c11f12d89186ed91ffd41a1dcbc2b12189f73cc1 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -375,10 +375,14 @@ impl Frame { // Reuse entries in the depth map that didn't change since the last frame. for (order, view_id, bounds) in prev_frame.depth_map.drain(..) { if self.reused_views.contains(&view_id) { - self.depth_map.push((order, view_id, bounds)); + match self + .depth_map + .binary_search_by(|(level, _, _)| order.cmp(level)) + { + Ok(i) | Err(i) => self.depth_map.insert(i, (order, view_id, bounds)), + } } } - self.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 prev_frame.element_states.drain() { @@ -1052,10 +1056,10 @@ impl<'a> WindowContext<'a> { pub fn add_opaque_layer(&mut self, bounds: Bounds) { let stacking_order = self.window.next_frame.z_index_stack.clone(); let view_id = self.parent_view_id(); - self.window - .next_frame - .depth_map - .push((stacking_order, view_id, bounds)); + 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, view_id, bounds)), + } } /// Returns true if there is no opaque layer containing the given point From a32ad3f9074026f1a7bf13e96b8534095e4bfc31 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 12 Jan 2024 14:50:42 +0100 Subject: [PATCH 19/22] Fix editor tests --- crates/editor/src/element.rs | 58 ++++++++++++++++++++---------------- crates/gpui/src/window.rs | 12 ++++---- 2 files changed, 38 insertions(+), 32 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 29d3e0aaea38011a120401eb54aabb1a311bd3b6..fcf2ff36108b319b7b18990e0c9280ad8b752f6e 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -3404,14 +3404,16 @@ mod tests { }) .unwrap(); let state = cx - .update_window(window.into(), |_, cx| { - element.compute_layout( - Bounds { - origin: point(px(500.), px(500.)), - size: size(px(500.), px(500.)), - }, - cx, - ) + .update_window(window.into(), |view, cx| { + cx.with_view_id(view.entity_id(), |cx| { + element.compute_layout( + Bounds { + origin: point(px(500.), px(500.)), + size: size(px(500.), px(500.)), + }, + cx, + ) + }) }) .unwrap(); @@ -3496,14 +3498,16 @@ mod tests { }); let state = cx - .update_window(window.into(), |_, cx| { - element.compute_layout( - Bounds { - origin: point(px(500.), px(500.)), - size: size(px(500.), px(500.)), - }, - cx, - ) + .update_window(window.into(), |view, cx| { + cx.with_view_id(view.entity_id(), |cx| { + element.compute_layout( + Bounds { + origin: point(px(500.), px(500.)), + size: size(px(500.), px(500.)), + }, + cx, + ) + }) }) .unwrap(); assert_eq!(state.selections.len(), 1); @@ -3558,14 +3562,16 @@ mod tests { let mut element = EditorElement::new(&editor, style); let state = cx - .update_window(window.into(), |_, cx| { - element.compute_layout( - Bounds { - origin: point(px(500.), px(500.)), - size: size(px(500.), px(500.)), - }, - cx, - ) + .update_window(window.into(), |view, cx| { + cx.with_view_id(view.entity_id(), |cx| { + element.compute_layout( + Bounds { + origin: point(px(500.), px(500.)), + size: size(px(500.), px(500.)), + }, + cx, + ) + }) }) .unwrap(); let size = state.position_map.size; @@ -3582,8 +3588,8 @@ mod tests { // Don't panic. let bounds = Bounds::::new(Default::default(), size); - cx.update_window(window.into(), |_, cx| { - element.paint(bounds, &mut (), cx); + cx.update_window(window.into(), |view, cx| { + cx.paint_view(view.entity_id(), |cx| element.paint(bounds, &mut (), cx)) }) .unwrap() } diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index f34342899660ac65b0206cbf582b5790e761f9be..bc5e7ee765941cca2af6b875ffaac1fbc68cf58a 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -2004,11 +2004,9 @@ impl<'a> WindowContext<'a> { result } - pub(crate) fn with_view_id( - &mut self, - view_id: EntityId, - f: impl FnOnce(&mut Self) -> R, - ) -> R { + /// Invoke the given function with the given view id present on the view stack. + /// This is a fairly low-level method used to layout views. + pub fn with_view_id(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R { let text_system = self.text_system().clone(); text_system.with_view(view_id, || { self.window.next_frame.view_stack.push(view_id); @@ -2018,7 +2016,9 @@ impl<'a> WindowContext<'a> { }) } - pub(crate) fn paint_view(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R { + /// Invoke the given function with the given view id present on the view stack. + /// This is a fairly low-level method used to paint views. + pub fn paint_view(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R { self.with_view_id(view_id, |cx| { cx.window .next_frame From 817b641c17d2ab5aed2debed61e1dda269690540 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 12 Jan 2024 17:36:11 +0100 Subject: [PATCH 20/22] Ensure editor elements invalidate their parent views on notify Co-Authored-By: Nathan Sobo Co-Authored-By: Conrad Irwin --- crates/editor/src/element.rs | 201 ++++++++++++++++++----------------- crates/gpui/src/window.rs | 34 +++--- 2 files changed, 125 insertions(+), 110 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index fcf2ff36108b319b7b18990e0c9280ad8b752f6e..a2f1b0c09dd1f48bef0d8111054756d48e433589 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -26,11 +26,11 @@ use git::diff::DiffHunkStatus; use gpui::{ div, fill, outline, overlay, point, px, quad, relative, size, transparent_black, Action, AnchorCorner, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Corners, - CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Hsla, InteractiveBounds, - InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton, MouseDownEvent, - MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine, - SharedString, Size, StackingOrder, StatefulInteractiveElement, Style, Styled, TextRun, - TextStyle, View, ViewContext, WindowContext, + CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Entity, Hsla, + InteractiveBounds, InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton, + MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, ScrollDelta, + ScrollWheelEvent, ShapedLine, SharedString, Size, StackingOrder, StatefulInteractiveElement, + Style, Styled, TextRun, TextStyle, View, ViewContext, WindowContext, }; use itertools::Itertools; use language::language_settings::ShowWhitespaceSetting; @@ -2801,44 +2801,49 @@ impl Element for EditorElement { _element_state: Option, cx: &mut gpui::WindowContext, ) -> (gpui::LayoutId, Self::State) { - self.editor.update(cx, |editor, cx| { - editor.set_style(self.style.clone(), cx); - - let layout_id = match editor.mode { - EditorMode::SingleLine => { - let rem_size = cx.rem_size(); - let mut style = Style::default(); - style.size.width = relative(1.).into(); - style.size.height = self.style.text.line_height_in_pixels(rem_size).into(); - cx.request_layout(&style, None) - } - EditorMode::AutoHeight { max_lines } => { - let editor_handle = cx.view().clone(); - let max_line_number_width = - self.max_line_number_width(&editor.snapshot(cx), cx); - cx.request_measured_layout(Style::default(), move |known_dimensions, _, cx| { - editor_handle - .update(cx, |editor, cx| { - compute_auto_height_layout( - editor, - max_lines, - max_line_number_width, - known_dimensions, - cx, - ) - }) - .unwrap_or_default() - }) - } - EditorMode::Full => { - let mut style = Style::default(); - style.size.width = relative(1.).into(); - style.size.height = relative(1.).into(); - cx.request_layout(&style, None) - } - }; + cx.with_view_id(self.editor.entity_id(), |cx| { + self.editor.update(cx, |editor, cx| { + editor.set_style(self.style.clone(), cx); + + let layout_id = match editor.mode { + EditorMode::SingleLine => { + let rem_size = cx.rem_size(); + let mut style = Style::default(); + style.size.width = relative(1.).into(); + style.size.height = self.style.text.line_height_in_pixels(rem_size).into(); + cx.request_layout(&style, None) + } + EditorMode::AutoHeight { max_lines } => { + let editor_handle = cx.view().clone(); + let max_line_number_width = + self.max_line_number_width(&editor.snapshot(cx), cx); + cx.request_measured_layout( + Style::default(), + move |known_dimensions, _, cx| { + editor_handle + .update(cx, |editor, cx| { + compute_auto_height_layout( + editor, + max_lines, + max_line_number_width, + known_dimensions, + cx, + ) + }) + .unwrap_or_default() + }, + ) + } + EditorMode::Full => { + let mut style = Style::default(); + style.size.width = relative(1.).into(); + style.size.height = relative(1.).into(); + cx.request_layout(&style, None) + } + }; - (layout_id, ()) + (layout_id, ()) + }) }) } @@ -2850,65 +2855,67 @@ impl Element for EditorElement { ) { let editor = self.editor.clone(); - cx.with_text_style( - Some(gpui::TextStyleRefinement { - font_size: Some(self.style.text.font_size), - ..Default::default() - }), - |cx| { - let mut layout = self.compute_layout(bounds, cx); - let gutter_bounds = Bounds { - origin: bounds.origin, - size: layout.gutter_size, - }; - let text_bounds = Bounds { - origin: gutter_bounds.upper_right(), - size: layout.text_size, - }; + cx.paint_view(self.editor.entity_id(), |cx| { + cx.with_text_style( + Some(gpui::TextStyleRefinement { + font_size: Some(self.style.text.font_size), + ..Default::default() + }), + |cx| { + let mut layout = self.compute_layout(bounds, cx); + let gutter_bounds = Bounds { + origin: bounds.origin, + size: layout.gutter_size, + }; + let text_bounds = Bounds { + origin: gutter_bounds.upper_right(), + size: layout.text_size, + }; - let focus_handle = editor.focus_handle(cx); - let key_context = self.editor.read(cx).key_context(cx); - cx.with_key_dispatch(Some(key_context), Some(focus_handle.clone()), |_, cx| { - self.register_actions(cx); - self.register_key_listeners(cx); + let focus_handle = editor.focus_handle(cx); + let key_context = self.editor.read(cx).key_context(cx); + cx.with_key_dispatch(Some(key_context), Some(focus_handle.clone()), |_, cx| { + self.register_actions(cx); + self.register_key_listeners(cx); - cx.with_content_mask(Some(ContentMask { bounds }), |cx| { - let input_handler = - ElementInputHandler::new(bounds, self.editor.clone(), cx); - cx.handle_input(&focus_handle, input_handler); + cx.with_content_mask(Some(ContentMask { bounds }), |cx| { + let input_handler = + ElementInputHandler::new(bounds, self.editor.clone(), cx); + cx.handle_input(&focus_handle, input_handler); - self.paint_background(gutter_bounds, text_bounds, &layout, cx); - if layout.gutter_size.width > Pixels::ZERO { - self.paint_gutter(gutter_bounds, &mut layout, cx); - } - self.paint_text(text_bounds, &mut layout, cx); + self.paint_background(gutter_bounds, text_bounds, &layout, cx); + if layout.gutter_size.width > Pixels::ZERO { + self.paint_gutter(gutter_bounds, &mut layout, cx); + } + self.paint_text(text_bounds, &mut layout, cx); - cx.with_z_index(0, |cx| { - self.paint_mouse_listeners( - bounds, - gutter_bounds, - text_bounds, - &layout, - cx, - ); - }); - if !layout.blocks.is_empty() { cx.with_z_index(0, |cx| { - cx.with_element_id(Some("editor_blocks"), |cx| { - self.paint_blocks(bounds, &mut layout, cx); - }); - }) - } + self.paint_mouse_listeners( + bounds, + gutter_bounds, + text_bounds, + &layout, + cx, + ); + }); + if !layout.blocks.is_empty() { + cx.with_z_index(0, |cx| { + cx.with_element_id(Some("editor_blocks"), |cx| { + self.paint_blocks(bounds, &mut layout, cx); + }); + }) + } - cx.with_z_index(1, |cx| { - self.paint_overlays(text_bounds, &mut layout, cx); - }); + cx.with_z_index(1, |cx| { + self.paint_overlays(text_bounds, &mut layout, cx); + }); - cx.with_z_index(2, |cx| self.paint_scrollbar(bounds, &mut layout, cx)); - }); - }) - }, - ); + cx.with_z_index(2, |cx| self.paint_scrollbar(bounds, &mut layout, cx)); + }); + }) + }, + ) + }) } } @@ -3588,10 +3595,8 @@ mod tests { // Don't panic. let bounds = Bounds::::new(Default::default(), size); - cx.update_window(window.into(), |view, cx| { - cx.paint_view(view.entity_id(), |cx| element.paint(bounds, &mut (), cx)) - }) - .unwrap() + cx.update_window(window.into(), |view, cx| element.paint(bounds, &mut (), cx)) + .unwrap() } #[gpui::test] diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index bc5e7ee765941cca2af6b875ffaac1fbc68cf58a..619a1bfeb6c23071a1c7e8071b396eb908bcf0f8 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -2009,24 +2009,34 @@ impl<'a> WindowContext<'a> { pub fn with_view_id(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R { let text_system = self.text_system().clone(); text_system.with_view(view_id, || { - self.window.next_frame.view_stack.push(view_id); - let result = f(self); - self.window.next_frame.view_stack.pop(); - result + if self.window.next_frame.view_stack.last() == Some(&view_id) { + return f(self); + } else { + self.window.next_frame.view_stack.push(view_id); + let result = f(self); + self.window.next_frame.view_stack.pop(); + result + } }) } /// Invoke the given function with the given view id present on the view stack. /// This is a fairly low-level method used to paint views. pub fn paint_view(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R { - self.with_view_id(view_id, |cx| { - cx.window - .next_frame - .dispatch_tree - .push_node(None, None, Some(view_id)); - let result = f(cx); - cx.window.next_frame.dispatch_tree.pop_node(); - result + let text_system = self.text_system().clone(); + text_system.with_view(view_id, || { + if self.window.next_frame.view_stack.last() == Some(&view_id) { + return f(self); + } else { + self.window.next_frame.view_stack.push(view_id); + self.window + .next_frame + .dispatch_tree + .push_node(None, None, Some(view_id)); + let result = f(self); + self.window.next_frame.view_stack.pop(); + result + } }) } From f4a7f6f4c3cf0d257387464bef1c107b0c127258 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 12 Jan 2024 18:06:18 +0100 Subject: [PATCH 21/22] Fix warning --- crates/editor/src/element.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index a2f1b0c09dd1f48bef0d8111054756d48e433589..f5c016a7c78cbee00bd5e606b3f8999fcb1a3661 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -3595,7 +3595,7 @@ mod tests { // Don't panic. let bounds = Bounds::::new(Default::default(), size); - cx.update_window(window.into(), |view, cx| element.paint(bounds, &mut (), cx)) + cx.update_window(window.into(), |_, cx| element.paint(bounds, &mut (), cx)) .unwrap() } From 05d05b051b17f64fc99fa1b4888761caba0058d5 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 15 Jan 2024 11:40:42 +0100 Subject: [PATCH 22/22] Pop node from dispatch tree during `cx.paint_view` Co-Authored-By: Thorsten --- crates/gpui/src/window.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index b707066559dd5526b9430d298c35dbe843c1a6f9..8e38992251f5da196038f2718060d4f12a279496 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -2034,6 +2034,7 @@ impl<'a> WindowContext<'a> { .dispatch_tree .push_node(None, None, Some(view_id)); let result = f(self); + self.window.next_frame.dispatch_tree.pop_node(); self.window.next_frame.view_stack.pop(); result }