Overhaul the entire element system

Nathan Sobo created

Now the Element trait is designed to be wrapped in a Lifecycle enum that gets placed inside an ElementBox. This allows the framework to store data on behalf of the Element implementation, such as sizes, bounds, and also implementation-specific LayoutState and PaintState types. This makes it easier to reason about which data is available in each Element method.

Change summary

Cargo.lock                           |   7 
gpui/Cargo.toml                      |   1 
gpui/src/app.rs                      |  53 ++---
gpui/src/elements/align.rs           |  62 ++++---
gpui/src/elements/constrained_box.rs |  50 ++++--
gpui/src/elements/container.rs       |  59 ++++---
gpui/src/elements/empty.rs           |  48 +++--
gpui/src/elements/event_handler.rs   |  78 +++++----
gpui/src/elements/flex.rs            | 137 ++++++++++-------
gpui/src/elements/label.rs           |  64 +++----
gpui/src/elements/line_box.rs        |  66 ++++---
gpui/src/elements/mod.rs             |  52 +-----
gpui/src/elements/new.rs             | 191 +++++++++++++++++++++++
gpui/src/elements/stack.rs           |  58 ++++--
gpui/src/elements/svg.rs             |  49 +++---
gpui/src/elements/uniform_list.rs    | 177 ++++++++++++----------
gpui/src/lib.rs                      |   2 
gpui/src/platform/mac/renderer.rs    |   1 
gpui/src/platform/mac/window.rs      |   2 
gpui/src/presenter.rs                | 139 ++++++++--------
gpui/src/text_layout.rs              |   8 
zed/src/editor/buffer_element.rs     | 240 ++++++++++++++++-------------
zed/src/editor/buffer_view.rs        |   6 
zed/src/file_finder.rs               |   8 
zed/src/workspace/pane.rs            |   6 
zed/src/workspace/pane_group.rs      |   6 
zed/src/workspace/workspace_view.rs  |   3 
27 files changed, 931 insertions(+), 642 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -757,6 +757,7 @@ dependencies = [
  "pathfinder_geometry",
  "pin-project",
  "rand 0.8.3",
+ "replace_with",
  "smallvec",
  "smol",
  "tree-sitter",
@@ -1259,6 +1260,12 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "replace_with"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3a8614ee435691de62bcffcf4a66d91b3594bf1428a5722e79103249a095690"
+
 [[package]]
 name = "rust-argon2"
 version = "0.8.3"

gpui/Cargo.toml 🔗

@@ -14,6 +14,7 @@ pathfinder_color = "0.5"
 pathfinder_geometry = "0.5"
 pin-project = "1.0.5"
 rand = "0.8.3"
+replace_with = "0.1.7"
 smallvec = "1.6.1"
 smol = "1.2"
 tree-sitter = "0.17"

gpui/src/app.rs 🔗

@@ -1,5 +1,5 @@
 use crate::{
-    elements::Element,
+    elements::ElementBox,
     executor::{self, ForegroundTask},
     keymap::{self, Keystroke},
     platform::{self, App as _, WindowOptions},
@@ -30,7 +30,7 @@ pub trait Entity: 'static + Send + Sync {
 
 pub trait View: Entity {
     fn ui_name() -> &'static str;
-    fn render<'a>(&self, app: &AppContext) -> Box<dyn Element>;
+    fn render<'a>(&self, app: &AppContext) -> ElementBox;
     fn on_focus(&mut self, _ctx: &mut ViewContext<Self>) {}
     fn on_blur(&mut self, _ctx: &mut ViewContext<Self>) {}
     fn keymap_context(&self, _: &AppContext) -> keymap::Context {
@@ -458,11 +458,11 @@ impl MutableAppContext {
         self.ctx.focused_view_id(window_id)
     }
 
-    pub fn render_view(&self, window_id: usize, view_id: usize) -> Result<Box<dyn Element>> {
+    pub fn render_view(&self, window_id: usize, view_id: usize) -> Result<ElementBox> {
         self.ctx.render_view(window_id, view_id)
     }
 
-    pub fn render_views(&self, window_id: usize) -> Result<HashMap<usize, Box<dyn Element>>> {
+    pub fn render_views(&self, window_id: usize) -> Result<HashMap<usize, ElementBox>> {
         self.ctx.render_views(window_id)
     }
 
@@ -545,13 +545,6 @@ impl MutableAppContext {
         responder_chain: Vec<usize>,
         keystroke: &Keystroke,
     ) -> Result<bool> {
-        log::info!(
-            "dispatch_keystroke {} {:?} {:?}",
-            window_id,
-            responder_chain,
-            keystroke
-        );
-
         let mut context_chain = Vec::new();
         let mut context = keymap::Context::default();
         for view_id in &responder_chain {
@@ -641,7 +634,6 @@ impl MutableAppContext {
                     let mut app = self.upgrade();
                     let presenter = presenter.clone();
                     window.on_event(Box::new(move |event| {
-                        log::info!("event {:?}", event);
                         app.update(|ctx| {
                             if let Event::KeyDown { keystroke, .. } = &event {
                                 if ctx
@@ -687,7 +679,6 @@ impl MutableAppContext {
                 }
 
                 self.on_window_invalidated(window_id, move |invalidation, ctx| {
-                    log::info!("window invalidated");
                     let mut presenter = presenter.borrow_mut();
                     presenter.invalidate(invalidation, ctx.downgrade());
                     let scene = presenter.build_scene(window.size(), window.scale_factor(), ctx);
@@ -1285,7 +1276,7 @@ impl AppContext {
             .and_then(|window| window.focused_view)
     }
 
-    pub fn render_view(&self, window_id: usize, view_id: usize) -> Result<Box<dyn Element>> {
+    pub fn render_view(&self, window_id: usize, view_id: usize) -> Result<ElementBox> {
         self.windows
             .get(&window_id)
             .and_then(|w| w.views.get(&view_id))
@@ -1293,14 +1284,14 @@ impl AppContext {
             .ok_or(anyhow!("view not found"))
     }
 
-    pub fn render_views(&self, window_id: usize) -> Result<HashMap<usize, Box<dyn Element>>> {
+    pub fn render_views(&self, window_id: usize) -> Result<HashMap<usize, ElementBox>> {
         self.windows
             .get(&window_id)
             .map(|w| {
                 w.views
                     .iter()
                     .map(|(id, view)| (*id, view.render(self)))
-                    .collect::<HashMap<_, Box<dyn Element>>>()
+                    .collect::<HashMap<_, ElementBox>>()
             })
             .ok_or(anyhow!("window not found"))
     }
@@ -1388,7 +1379,7 @@ pub trait AnyView: Send + Sync {
     fn as_any(&self) -> &dyn Any;
     fn as_any_mut(&mut self) -> &mut dyn Any;
     fn ui_name(&self) -> &'static str;
-    fn render<'a>(&self, app: &AppContext) -> Box<dyn Element>;
+    fn render<'a>(&self, app: &AppContext) -> ElementBox;
     fn on_focus(&mut self, app: &mut MutableAppContext, window_id: usize, view_id: usize);
     fn on_blur(&mut self, app: &mut MutableAppContext, window_id: usize, view_id: usize);
     fn keymap_context(&self, app: &AppContext) -> keymap::Context;
@@ -1410,7 +1401,7 @@ where
         T::ui_name()
     }
 
-    fn render<'a>(&self, app: &AppContext) -> Box<dyn Element> {
+    fn render<'a>(&self, app: &AppContext) -> ElementBox {
         View::render(self, app)
     }
 
@@ -2556,7 +2547,7 @@ mod tests {
         }
 
         impl super::View for View {
-            fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
+            fn render<'a>(&self, _: &AppContext) -> ElementBox {
                 Empty::new().boxed()
             }
 
@@ -2628,7 +2619,7 @@ mod tests {
         }
 
         impl super::View for View {
-            fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
+            fn render<'a>(&self, _: &AppContext) -> ElementBox {
                 Empty::new().boxed()
             }
 
@@ -2684,7 +2675,7 @@ mod tests {
         }
 
         impl super::View for View {
-            fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
+            fn render<'a>(&self, _: &AppContext) -> ElementBox {
                 Empty::new().boxed()
             }
 
@@ -2738,7 +2729,7 @@ mod tests {
         }
 
         impl super::View for View {
-            fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
+            fn render<'a>(&self, _: &AppContext) -> ElementBox {
                 Empty::new().boxed()
             }
 
@@ -2784,7 +2775,7 @@ mod tests {
         }
 
         impl super::View for View {
-            fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
+            fn render<'a>(&self, _: &AppContext) -> ElementBox {
                 Empty::new().boxed()
             }
 
@@ -2835,7 +2826,7 @@ mod tests {
         }
 
         impl super::View for View {
-            fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
+            fn render<'a>(&self, _: &AppContext) -> ElementBox {
                 Empty::new().boxed()
             }
 
@@ -2897,7 +2888,7 @@ mod tests {
         }
 
         impl super::View for View {
-            fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
+            fn render<'a>(&self, _: &AppContext) -> ElementBox {
                 Empty::new().boxed()
             }
 
@@ -2939,7 +2930,7 @@ mod tests {
         }
 
         impl super::View for View {
-            fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
+            fn render<'a>(&self, _: &AppContext) -> ElementBox {
                 Empty::new().boxed()
             }
 
@@ -2981,7 +2972,7 @@ mod tests {
         }
 
         impl View for ViewA {
-            fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
+            fn render<'a>(&self, _: &AppContext) -> ElementBox {
                 Empty::new().boxed()
             }
 
@@ -2999,7 +2990,7 @@ mod tests {
         }
 
         impl View for ViewB {
-            fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
+            fn render<'a>(&self, _: &AppContext) -> ElementBox {
                 Empty::new().boxed()
             }
 
@@ -3104,7 +3095,7 @@ mod tests {
         }
 
         impl super::View for View {
-            fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
+            fn render<'a>(&self, _: &AppContext) -> ElementBox {
                 Empty::new().boxed()
             }
 
@@ -3174,7 +3165,7 @@ mod tests {
     //     }
 
     //     impl super::View for View {
-    //         fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
+    //         fn render<'a>(&self, _: &AppContext) -> ElementBox {
     //             Empty::new().boxed()
     //         }
 
@@ -3253,7 +3244,7 @@ mod tests {
         }
 
         impl super::View for View {
-            fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
+            fn render<'a>(&self, _: &AppContext) -> ElementBox {
                 Empty::new().boxed()
             }
 

gpui/src/elements/align.rs 🔗

@@ -1,21 +1,19 @@
 use crate::{
-    AfterLayoutContext, AppContext, Element, Event, EventContext, LayoutContext, MutableAppContext,
-    PaintContext, SizeConstraint,
+    AfterLayoutContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
+    SizeConstraint,
 };
 use pathfinder_geometry::vector::{vec2f, Vector2F};
 
 pub struct Align {
-    child: Box<dyn Element>,
+    child: ElementBox,
     alignment: Vector2F,
-    size: Option<Vector2F>,
 }
 
 impl Align {
-    pub fn new(child: Box<dyn Element>) -> Self {
+    pub fn new(child: ElementBox) -> Self {
         Self {
             child,
             alignment: Vector2F::zero(),
-            size: None,
         }
     }
 
@@ -26,43 +24,59 @@ impl Align {
 }
 
 impl Element for Align {
+    type LayoutState = ();
+    type PaintState = ();
+
     fn layout(
         &mut self,
         mut constraint: SizeConstraint,
         ctx: &mut LayoutContext,
-        app: &AppContext,
-    ) -> Vector2F {
+    ) -> (Vector2F, Self::LayoutState) {
         let mut size = constraint.max;
         constraint.min = Vector2F::zero();
-        let child_size = self.child.layout(constraint, ctx, app);
+        let child_size = self.child.layout(constraint, ctx);
         if size.x().is_infinite() {
             size.set_x(child_size.x());
         }
         if size.y().is_infinite() {
             size.set_y(child_size.y());
         }
-        self.size = Some(size);
-        size
+        (size, ())
     }
 
-    fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &mut MutableAppContext) {
-        self.child.after_layout(ctx, app);
+    fn after_layout(
+        &mut self,
+        _: Vector2F,
+        _: &mut Self::LayoutState,
+        ctx: &mut AfterLayoutContext,
+    ) {
+        self.child.after_layout(ctx);
     }
 
-    fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) {
-        let self_center = self.size.unwrap() / 2.0;
-        let self_target = self_center + self_center * self.alignment;
-        let child_center = self.child.size().unwrap() / 2.0;
+    fn paint(
+        &mut self,
+        bounds: pathfinder_geometry::rect::RectF,
+        _: &mut Self::LayoutState,
+        ctx: &mut PaintContext,
+    ) -> Self::PaintState {
+        let my_center = bounds.size() / 2.;
+        let my_target = my_center + my_center * self.alignment;
+
+        let child_center = self.child.size() / 2.;
         let child_target = child_center + child_center * self.alignment;
-        let origin = origin - (child_target - self_target);
-        self.child.paint(origin, ctx, app);
-    }
 
-    fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool {
-        self.child.dispatch_event(event, ctx, app)
+        self.child
+            .paint(bounds.origin() - (child_target - my_target), ctx);
     }
 
-    fn size(&self) -> Option<Vector2F> {
-        self.size
+    fn dispatch_event(
+        &mut self,
+        event: &Event,
+        _: pathfinder_geometry::rect::RectF,
+        _: &mut Self::LayoutState,
+        _: &mut Self::PaintState,
+        ctx: &mut EventContext,
+    ) -> bool {
+        self.child.dispatch_event(event, ctx)
     }
 }

gpui/src/elements/constrained_box.rs 🔗

@@ -1,16 +1,16 @@
 use crate::{
-    AfterLayoutContext, AppContext, Element, Event, EventContext, LayoutContext, MutableAppContext,
-    PaintContext, SizeConstraint,
+    AfterLayoutContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
+    SizeConstraint,
 };
 use pathfinder_geometry::vector::Vector2F;
 
 pub struct ConstrainedBox {
-    child: Box<dyn Element>,
+    child: ElementBox,
     constraint: SizeConstraint,
 }
 
 impl ConstrainedBox {
-    pub fn new(child: Box<dyn Element>) -> Self {
+    pub fn new(child: ElementBox) -> Self {
         Self {
             child,
             constraint: SizeConstraint {
@@ -38,30 +38,46 @@ impl ConstrainedBox {
 }
 
 impl Element for ConstrainedBox {
+    type LayoutState = ();
+    type PaintState = ();
+
     fn layout(
         &mut self,
         mut constraint: SizeConstraint,
         ctx: &mut LayoutContext,
-        app: &AppContext,
-    ) -> Vector2F {
+    ) -> (Vector2F, Self::LayoutState) {
         constraint.min = constraint.min.max(self.constraint.min);
         constraint.max = constraint.max.min(self.constraint.max);
-        self.child.layout(constraint, ctx, app)
-    }
-
-    fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &mut MutableAppContext) {
-        self.child.after_layout(ctx, app);
+        let size = self.child.layout(constraint, ctx);
+        (size, ())
     }
 
-    fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) {
-        self.child.paint(origin, ctx, app);
+    fn after_layout(
+        &mut self,
+        _: Vector2F,
+        _: &mut Self::LayoutState,
+        ctx: &mut AfterLayoutContext,
+    ) {
+        self.child.after_layout(ctx);
     }
 
-    fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool {
-        self.child.dispatch_event(event, ctx, app)
+    fn paint(
+        &mut self,
+        bounds: pathfinder_geometry::rect::RectF,
+        _: &mut Self::LayoutState,
+        ctx: &mut PaintContext,
+    ) -> Self::PaintState {
+        self.child.paint(bounds.origin(), ctx);
     }
 
-    fn size(&self) -> Option<Vector2F> {
-        self.child.size()
+    fn dispatch_event(
+        &mut self,
+        event: &Event,
+        _: pathfinder_geometry::rect::RectF,
+        _: &mut Self::LayoutState,
+        _: &mut Self::PaintState,
+        ctx: &mut EventContext,
+    ) -> bool {
+        self.child.dispatch_event(event, ctx)
     }
 }

gpui/src/elements/container.rs 🔗

@@ -4,8 +4,8 @@ use crate::{
     color::ColorU,
     geometry::vector::{vec2f, Vector2F},
     scene::{Border, Quad},
-    AfterLayoutContext, AppContext, Element, Event, EventContext, LayoutContext, MutableAppContext,
-    PaintContext, SizeConstraint,
+    AfterLayoutContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
+    SizeConstraint,
 };
 
 pub struct Container {
@@ -16,13 +16,11 @@ pub struct Container {
     border: Border,
     corner_radius: f32,
     shadow: Option<Shadow>,
-    child: Box<dyn Element>,
-    size: Option<Vector2F>,
-    origin: Option<Vector2F>,
+    child: ElementBox,
 }
 
 impl Container {
-    pub fn new(child: Box<dyn Element>) -> Self {
+    pub fn new(child: ElementBox) -> Self {
         Self {
             margin: Margin::default(),
             padding: Padding::default(),
@@ -32,8 +30,6 @@ impl Container {
             corner_radius: 0.0,
             shadow: None,
             child,
-            size: None,
-            origin: None,
         }
     }
 
@@ -122,43 +118,56 @@ impl Container {
 }
 
 impl Element for Container {
+    type LayoutState = ();
+    type PaintState = ();
+
     fn layout(
         &mut self,
         constraint: SizeConstraint,
         ctx: &mut LayoutContext,
-        app: &AppContext,
-    ) -> Vector2F {
+    ) -> (Vector2F, Self::LayoutState) {
         let size_buffer = self.margin_size() + self.padding_size() + self.border_size();
         let child_constraint = SizeConstraint {
             min: (constraint.min - size_buffer).max(Vector2F::zero()),
             max: (constraint.max - size_buffer).max(Vector2F::zero()),
         };
-        let child_size = self.child.layout(child_constraint, ctx, app);
-        let size = child_size + size_buffer;
-        self.size = Some(size);
-        size
+        let child_size = self.child.layout(child_constraint, ctx);
+        (child_size + size_buffer, ())
     }
 
-    fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &mut MutableAppContext) {
-        self.child.after_layout(ctx, app);
+    fn after_layout(
+        &mut self,
+        _: Vector2F,
+        _: &mut Self::LayoutState,
+        ctx: &mut AfterLayoutContext,
+    ) {
+        self.child.after_layout(ctx);
     }
 
-    fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) {
+    fn paint(
+        &mut self,
+        bounds: RectF,
+        _: &mut Self::LayoutState,
+        ctx: &mut PaintContext,
+    ) -> Self::PaintState {
         ctx.scene.push_quad(Quad {
-            bounds: RectF::new(origin, self.size.unwrap()),
+            bounds,
             background: self.background_color,
             border: self.border,
             corner_radius: self.corner_radius,
         });
-        self.child.paint(origin, ctx, app);
+        self.child.paint(bounds.origin(), ctx);
     }
 
-    fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool {
-        self.child.dispatch_event(event, ctx, app)
-    }
-
-    fn size(&self) -> Option<Vector2F> {
-        self.size
+    fn dispatch_event(
+        &mut self,
+        event: &Event,
+        _: RectF,
+        _: &mut Self::LayoutState,
+        _: &mut Self::PaintState,
+        ctx: &mut EventContext,
+    ) -> bool {
+        self.child.dispatch_event(event, ctx)
     }
 }
 

gpui/src/elements/empty.rs 🔗

@@ -1,45 +1,47 @@
 use crate::{
-    AfterLayoutContext, AppContext, Element, Event, EventContext, LayoutContext, MutableAppContext,
-    PaintContext, SizeConstraint,
+    AfterLayoutContext, Element, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
 };
-use pathfinder_geometry::vector::Vector2F;
+use pathfinder_geometry::{rect::RectF, vector::Vector2F};
 
-pub struct Empty {
-    size: Option<Vector2F>,
-    origin: Option<Vector2F>,
-}
+pub struct Empty;
 
 impl Empty {
     pub fn new() -> Self {
-        Self {
-            size: None,
-            origin: None,
-        }
+        Self
     }
 }
 
 impl Element for Empty {
+    type LayoutState = ();
+    type PaintState = ();
+
     fn layout(
         &mut self,
         constraint: SizeConstraint,
         _: &mut LayoutContext,
-        _: &AppContext,
-    ) -> Vector2F {
-        self.size = Some(constraint.max);
-        constraint.max
+    ) -> (Vector2F, Self::LayoutState) {
+        (constraint.max, ())
     }
 
-    fn after_layout(&mut self, _: &mut AfterLayoutContext, _: &mut MutableAppContext) {}
-
-    fn paint(&mut self, origin: Vector2F, _: &mut PaintContext, _: &AppContext) {
-        self.origin = Some(origin);
+    fn after_layout(&mut self, _: Vector2F, _: &mut Self::LayoutState, _: &mut AfterLayoutContext) {
     }
 
-    fn dispatch_event(&self, _: &Event, _: &mut EventContext, _: &AppContext) -> bool {
-        false
+    fn paint(
+        &mut self,
+        _: RectF,
+        _: &mut Self::LayoutState,
+        _: &mut PaintContext,
+    ) -> Self::PaintState {
     }
 
-    fn size(&self) -> Option<Vector2F> {
-        self.size
+    fn dispatch_event(
+        &mut self,
+        _: &Event,
+        _: RectF,
+        _: &mut Self::LayoutState,
+        _: &mut Self::PaintState,
+        _: &mut EventContext,
+    ) -> bool {
+        false
     }
 }

gpui/src/elements/event_handler.rs 🔗

@@ -1,69 +1,83 @@
-use super::try_rect;
 use crate::{
-    geometry::vector::Vector2F, AfterLayoutContext, AppContext, Element, Event, EventContext,
-    LayoutContext, MutableAppContext, PaintContext, SizeConstraint,
+    geometry::vector::Vector2F, AfterLayoutContext, Element, ElementBox, Event, EventContext,
+    LayoutContext, PaintContext, SizeConstraint,
 };
-use std::cell::RefCell;
 
 pub struct EventHandler {
-    child: Box<dyn Element>,
-    mouse_down: Option<RefCell<Box<dyn FnMut(&mut EventContext, &AppContext) -> bool>>>,
-    origin: Option<Vector2F>,
+    child: ElementBox,
+    mouse_down: Option<Box<dyn FnMut(&mut EventContext) -> bool>>,
 }
 
 impl EventHandler {
-    pub fn new(child: Box<dyn Element>) -> Self {
+    pub fn new(child: ElementBox) -> Self {
         Self {
             child,
             mouse_down: None,
-            origin: None,
         }
     }
 
     pub fn on_mouse_down<F>(mut self, callback: F) -> Self
     where
-        F: 'static + FnMut(&mut EventContext, &AppContext) -> bool,
+        F: 'static + FnMut(&mut EventContext) -> bool,
     {
-        self.mouse_down = Some(RefCell::new(Box::new(callback)));
+        self.mouse_down = Some(Box::new(callback));
         self
     }
 }
 
 impl Element for EventHandler {
+    type LayoutState = ();
+    type PaintState = ();
+
     fn layout(
         &mut self,
         constraint: SizeConstraint,
         ctx: &mut LayoutContext,
-        app: &AppContext,
-    ) -> Vector2F {
-        self.child.layout(constraint, ctx, app)
-    }
-
-    fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &mut MutableAppContext) {
-        self.child.after_layout(ctx, app);
+    ) -> (Vector2F, Self::LayoutState) {
+        let size = self.child.layout(constraint, ctx);
+        (size, ())
     }
 
-    fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) {
-        self.origin = Some(origin);
-        self.child.paint(origin, ctx, app);
+    fn after_layout(
+        &mut self,
+        _: Vector2F,
+        _: &mut Self::LayoutState,
+        ctx: &mut AfterLayoutContext,
+    ) {
+        self.child.after_layout(ctx);
     }
 
-    fn size(&self) -> Option<Vector2F> {
-        self.child.size()
+    fn paint(
+        &mut self,
+        bounds: pathfinder_geometry::rect::RectF,
+        _: &mut Self::LayoutState,
+        ctx: &mut PaintContext,
+    ) -> Self::PaintState {
+        self.child.paint(bounds.origin(), ctx);
     }
 
-    fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool {
-        match event {
-            Event::LeftMouseDown { position, .. } => {
-                if let Some(callback) = self.mouse_down.as_ref() {
-                    let rect = try_rect(self.origin, self.size()).unwrap();
-                    if rect.contains_point(*position) {
-                        return callback.borrow_mut()(ctx, app);
+    fn dispatch_event(
+        &mut self,
+        event: &Event,
+        bounds: pathfinder_geometry::rect::RectF,
+        _: &mut Self::LayoutState,
+        _: &mut Self::PaintState,
+        ctx: &mut EventContext,
+    ) -> bool {
+        if self.child.dispatch_event(event, ctx) {
+            true
+        } else {
+            match event {
+                Event::LeftMouseDown { position, .. } => {
+                    if let Some(callback) = self.mouse_down.as_mut() {
+                        if bounds.contains_point(*position) {
+                            return callback(ctx);
+                        }
                     }
+                    false
                 }
-                false
+                _ => false,
             }
-            _ => false,
         }
     }
 }

gpui/src/elements/flex.rs 🔗

@@ -1,15 +1,14 @@
+use std::any::Any;
+
 use crate::{
-    AfterLayoutContext, AppContext, Axis, Element, Event, EventContext, LayoutContext,
-    MutableAppContext, PaintContext, SizeConstraint, Vector2FExt,
+    AfterLayoutContext, Axis, Element, ElementBox, Event, EventContext, LayoutContext,
+    PaintContext, SizeConstraint, Vector2FExt,
 };
 use pathfinder_geometry::vector::{vec2f, Vector2F};
-use std::any::Any;
 
 pub struct Flex {
     axis: Axis,
-    children: Vec<Box<dyn Element>>,
-    size: Option<Vector2F>,
-    origin: Option<Vector2F>,
+    children: Vec<ElementBox>,
 }
 
 impl Flex {
@@ -17,8 +16,6 @@ impl Flex {
         Self {
             axis,
             children: Default::default(),
-            size: None,
-            origin: None,
         }
     }
 
@@ -30,39 +27,41 @@ impl Flex {
         Self::new(Axis::Vertical)
     }
 
-    fn child_flex<'b>(child: &dyn Element) -> Option<f32> {
+    fn child_flex<'b>(child: &ElementBox) -> Option<f32> {
         child
-            .parent_data()
+            .metadata()
             .and_then(|d| d.downcast_ref::<FlexParentData>())
             .map(|data| data.flex)
     }
 }
 
-impl Extend<Box<dyn Element>> for Flex {
-    fn extend<T: IntoIterator<Item = Box<dyn Element>>>(&mut self, children: T) {
+impl Extend<ElementBox> for Flex {
+    fn extend<T: IntoIterator<Item = ElementBox>>(&mut self, children: T) {
         self.children.extend(children);
     }
 }
 
 impl Element for Flex {
+    type LayoutState = ();
+    type PaintState = ();
+
     fn layout(
         &mut self,
         constraint: SizeConstraint,
         ctx: &mut LayoutContext,
-        app: &AppContext,
-    ) -> Vector2F {
+    ) -> (Vector2F, Self::LayoutState) {
         let mut total_flex = 0.0;
         let mut fixed_space = 0.0;
 
         let cross_axis = self.axis.invert();
         let mut cross_axis_max: f32 = 0.0;
         for child in &mut self.children {
-            if let Some(flex) = Self::child_flex(child.as_ref()) {
+            if let Some(flex) = Self::child_flex(&child) {
                 total_flex += flex;
             } else {
                 let child_constraint =
                     SizeConstraint::strict_along(cross_axis, constraint.max_along(cross_axis));
-                let size = child.layout(child_constraint, ctx, app);
+                let size = child.layout(child_constraint, ctx);
                 fixed_space += size.along(self.axis);
                 cross_axis_max = cross_axis_max.max(size.along(cross_axis));
             }
@@ -77,7 +76,7 @@ impl Element for Flex {
             let mut remaining_flex = total_flex;
             for child in &mut self.children {
                 let space_per_flex = remaining_space / remaining_flex;
-                if let Some(flex) = Self::child_flex(child.as_ref()) {
+                if let Some(flex) = Self::child_flex(&child) {
                     let child_max = space_per_flex * flex;
                     let child_constraint = match self.axis {
                         Axis::Horizontal => SizeConstraint::new(
@@ -89,7 +88,7 @@ impl Element for Flex {
                             vec2f(constraint.max.x(), child_max),
                         ),
                     };
-                    let child_size = child.layout(child_constraint, ctx, app);
+                    let child_size = child.layout(child_constraint, ctx);
                     remaining_space -= child_size.along(self.axis);
                     remaining_flex -= flex;
                     cross_axis_max = cross_axis_max.max(child_size.along(cross_axis));
@@ -110,45 +109,55 @@ impl Element for Flex {
         if constraint.min.x().is_finite() {
             size.set_x(size.x().max(constraint.min.x()));
         }
+
         if constraint.min.y().is_finite() {
             size.set_y(size.y().max(constraint.min.y()));
         }
 
-        self.size = Some(size);
-        size
+        (size, ())
     }
 
-    fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &mut MutableAppContext) {
+    fn after_layout(
+        &mut self,
+        _: Vector2F,
+        _: &mut Self::LayoutState,
+        ctx: &mut AfterLayoutContext,
+    ) {
         for child in &mut self.children {
-            child.after_layout(ctx, app);
+            child.after_layout(ctx);
         }
     }
 
-    fn paint(&mut self, mut origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) {
-        self.origin = Some(origin);
-
+    fn paint(
+        &mut self,
+        bounds: pathfinder_geometry::rect::RectF,
+        _: &mut Self::LayoutState,
+        ctx: &mut PaintContext,
+    ) -> Self::PaintState {
+        let mut child_origin = bounds.origin();
         for child in &mut self.children {
-            child.paint(origin, ctx, app);
+            child.paint(child_origin, ctx);
             match self.axis {
-                Axis::Horizontal => origin += vec2f(child.size().unwrap().x(), 0.0),
-                Axis::Vertical => origin += vec2f(0.0, child.size().unwrap().y()),
+                Axis::Horizontal => child_origin += vec2f(child.size().x(), 0.0),
+                Axis::Vertical => child_origin += vec2f(0.0, child.size().y()),
             }
         }
     }
 
-    fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool {
+    fn dispatch_event(
+        &mut self,
+        event: &Event,
+        _: pathfinder_geometry::rect::RectF,
+        _: &mut Self::LayoutState,
+        _: &mut Self::PaintState,
+        ctx: &mut EventContext,
+    ) -> bool {
         let mut handled = false;
-        for child in &self.children {
-            if child.dispatch_event(event, ctx, app) {
-                handled = true;
-            }
+        for child in &mut self.children {
+            handled = child.dispatch_event(event, ctx) || handled;
         }
         handled
     }
-
-    fn size(&self) -> Option<Vector2F> {
-        self.size
-    }
 }
 
 struct FlexParentData {
@@ -156,46 +165,62 @@ struct FlexParentData {
 }
 
 pub struct Expanded {
-    parent_data: FlexParentData,
-    child: Box<dyn Element>,
+    metadata: FlexParentData,
+    child: ElementBox,
 }
 
 impl Expanded {
-    pub fn new(flex: f32, child: Box<dyn Element>) -> Self {
+    pub fn new(flex: f32, child: ElementBox) -> Self {
         Expanded {
-            parent_data: FlexParentData { flex },
+            metadata: FlexParentData { flex },
             child,
         }
     }
 }
 
 impl Element for Expanded {
+    type LayoutState = ();
+    type PaintState = ();
+
     fn layout(
         &mut self,
         constraint: SizeConstraint,
         ctx: &mut LayoutContext,
-        app: &AppContext,
-    ) -> Vector2F {
-        self.child.layout(constraint, ctx, app)
+    ) -> (Vector2F, Self::LayoutState) {
+        let size = self.child.layout(constraint, ctx);
+        (size, ())
     }
 
-    fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &mut MutableAppContext) {
-        self.child.after_layout(ctx, app);
-    }
-
-    fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) {
-        self.child.paint(origin, ctx, app);
+    fn after_layout(
+        &mut self,
+        _: Vector2F,
+        _: &mut Self::LayoutState,
+        ctx: &mut AfterLayoutContext,
+    ) {
+        self.child.after_layout(ctx);
     }
 
-    fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool {
-        self.child.dispatch_event(event, ctx, app)
+    fn paint(
+        &mut self,
+        bounds: pathfinder_geometry::rect::RectF,
+        _: &mut Self::LayoutState,
+        ctx: &mut PaintContext,
+    ) -> Self::PaintState {
+        self.child.paint(bounds.origin(), ctx)
     }
 
-    fn size(&self) -> Option<Vector2F> {
-        self.child.size()
+    fn dispatch_event(
+        &mut self,
+        event: &Event,
+        _: pathfinder_geometry::rect::RectF,
+        _: &mut Self::LayoutState,
+        _: &mut Self::PaintState,
+        ctx: &mut EventContext,
+    ) -> bool {
+        self.child.dispatch_event(event, ctx)
     }
 
-    fn parent_data(&self) -> Option<&dyn Any> {
-        Some(&self.parent_data)
+    fn metadata(&self) -> Option<&dyn Any> {
+        Some(&self.metadata)
     }
 }

gpui/src/elements/label.rs 🔗

@@ -3,8 +3,7 @@ use crate::{
     fonts::{FamilyId, Properties},
     geometry::vector::{vec2f, Vector2F},
     text_layout::Line,
-    AfterLayoutContext, AppContext, Element, Event, EventContext, LayoutContext, MutableAppContext,
-    PaintContext, SizeConstraint,
+    AfterLayoutContext, Element, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
 };
 use std::{ops::Range, sync::Arc};
 
@@ -14,9 +13,11 @@ pub struct Label {
     font_properties: Properties,
     font_size: f32,
     highlights: Option<Highlights>,
-    layout_line: Option<Arc<Line>>,
-    colors: Option<Vec<(Range<usize>, ColorU)>>,
-    size: Option<Vector2F>,
+}
+
+pub struct LayoutState {
+    line: Arc<Line>,
+    colors: Vec<(Range<usize>, ColorU)>,
 }
 
 pub struct Highlights {
@@ -33,9 +34,6 @@ impl Label {
             font_properties: Properties::new(),
             font_size,
             highlights: None,
-            layout_line: None,
-            colors: None,
-            size: None,
         }
     }
 
@@ -55,12 +53,14 @@ impl Label {
 }
 
 impl Element for Label {
+    type LayoutState = LayoutState;
+    type PaintState = ();
+
     fn layout(
         &mut self,
         constraint: SizeConstraint,
         ctx: &mut LayoutContext,
-        _: &AppContext,
-    ) -> Vector2F {
+    ) -> (Vector2F, Self::LayoutState) {
         let font_id = ctx
             .font_cache
             .select_font(self.family_id, &self.font_properties)
@@ -109,9 +109,7 @@ impl Element for Label {
             colors = vec![(0..text_len, ColorU::black())];
         }
 
-        self.colors = Some(colors);
-
-        let layout_line = ctx.text_layout_cache.layout_str(
+        let line = ctx.text_layout_cache.layout_str(
             self.text.as_str(),
             self.font_size,
             styles.as_slice(),
@@ -119,37 +117,33 @@ impl Element for Label {
         );
 
         let size = vec2f(
-            layout_line
-                .width
-                .max(constraint.min.x())
-                .min(constraint.max.x()),
+            line.width.max(constraint.min.x()).min(constraint.max.x()),
             ctx.font_cache.line_height(font_id, self.font_size).ceil(),
         );
 
-        self.layout_line = Some(layout_line);
-        self.size = Some(size);
-
-        size
+        (size, LayoutState { line, colors })
     }
 
-    fn after_layout(&mut self, _: &mut AfterLayoutContext, _: &mut MutableAppContext) {}
-
-    fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, _: &AppContext) {
-        // ctx.canvas.set_fill_style(FillStyle::Color(ColorU::black()));
-        // self.layout_line.as_ref().unwrap().paint(
-        //     origin,
-        //     RectF::new(origin, self.size.unwrap()),
-        //     self.colors.as_ref().unwrap(),
-        //     ctx.canvas,
-        //     ctx.font_cache,
-        // );
+    fn after_layout(&mut self, _: Vector2F, _: &mut Self::LayoutState, _: &mut AfterLayoutContext) {
     }
 
-    fn size(&self) -> Option<Vector2F> {
-        self.size
+    fn paint(
+        &mut self,
+        bounds: pathfinder_geometry::rect::RectF,
+        layout: &mut Self::LayoutState,
+        ctx: &mut PaintContext,
+    ) -> Self::PaintState {
+        layout.line.paint(bounds, layout.colors.as_slice(), ctx)
     }
 
-    fn dispatch_event(&self, _: &Event, _: &mut EventContext, _: &AppContext) -> bool {
+    fn dispatch_event(
+        &mut self,
+        _: &Event,
+        _: pathfinder_geometry::rect::RectF,
+        _: &mut Self::LayoutState,
+        _: &mut Self::PaintState,
+        _: &mut EventContext,
+    ) -> bool {
         false
     }
 }

gpui/src/elements/line_box.rs 🔗

@@ -1,45 +1,42 @@
-use super::{AppContext, Element, MutableAppContext};
 use crate::{
     fonts::{FamilyId, FontId, Properties},
     geometry::vector::{vec2f, Vector2F},
-    AfterLayoutContext, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
+    AfterLayoutContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
+    SizeConstraint,
 };
 
 pub struct LineBox {
-    child: Box<dyn Element>,
+    child: ElementBox,
     family_id: FamilyId,
     font_size: f32,
     font_properties: Properties,
-    font_id: Option<FontId>,
-    size: Option<Vector2F>,
 }
 
 impl LineBox {
-    pub fn new(family_id: FamilyId, font_size: f32, child: Box<dyn Element>) -> Self {
+    pub fn new(family_id: FamilyId, font_size: f32, child: ElementBox) -> Self {
         Self {
             child,
             family_id,
             font_size,
             font_properties: Properties::default(),
-            font_id: None,
-            size: None,
         }
     }
 }
 
 impl Element for LineBox {
+    type LayoutState = Option<FontId>;
+    type PaintState = ();
+
     fn layout(
         &mut self,
         constraint: SizeConstraint,
         ctx: &mut LayoutContext,
-        app: &AppContext,
-    ) -> Vector2F {
+    ) -> (Vector2F, Self::LayoutState) {
         match ctx
             .font_cache
             .select_font(self.family_id, &self.font_properties)
         {
             Ok(font_id) => {
-                self.font_id = Some(font_id);
                 let line_height = ctx.font_cache.bounding_box(font_id, self.font_size).y();
                 let child_max = vec2f(
                     constraint.max.x(),
@@ -49,36 +46,47 @@ impl Element for LineBox {
                 let child_size = self.child.layout(
                     SizeConstraint::new(constraint.min.min(child_max), child_max),
                     ctx,
-                    app,
                 );
                 let size = vec2f(child_size.x(), line_height);
-                self.size = Some(size);
-                size
+                (size, Some(font_id))
             }
             Err(error) => {
-                log::error!("can't layout LineBox: {}", error);
-                self.size = Some(constraint.min);
-                constraint.min
+                log::error!("can't find font for LineBox: {}", error);
+                (constraint.min, None)
             }
         }
     }
 
-    fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &mut MutableAppContext) {
-        self.child.after_layout(ctx, app);
+    fn after_layout(
+        &mut self,
+        _: Vector2F,
+        _: &mut Self::LayoutState,
+        ctx: &mut AfterLayoutContext,
+    ) {
+        self.child.after_layout(ctx);
     }
 
-    fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) {
-        if let Some(font_id) = self.font_id {
-            let descent = ctx.font_cache.descent(font_id, self.font_size);
-            self.child.paint(origin + vec2f(0.0, -descent), ctx, app);
+    fn paint(
+        &mut self,
+        bounds: pathfinder_geometry::rect::RectF,
+        font_id: &mut Option<FontId>,
+        ctx: &mut PaintContext,
+    ) -> Self::PaintState {
+        if let Some(font_id) = font_id {
+            let descent = ctx.font_cache.descent(*font_id, self.font_size);
+            self.child
+                .paint(bounds.origin() + vec2f(0.0, -descent), ctx);
         }
     }
 
-    fn size(&self) -> Option<Vector2F> {
-        self.size
-    }
-
-    fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool {
-        self.child.dispatch_event(event, ctx, app)
+    fn dispatch_event(
+        &mut self,
+        event: &Event,
+        _: pathfinder_geometry::rect::RectF,
+        _: &mut Self::LayoutState,
+        _: &mut Self::PaintState,
+        ctx: &mut EventContext,
+    ) -> bool {
+        self.child.dispatch_event(event, ctx)
     }
 }

gpui/src/elements/mod.rs 🔗

@@ -6,6 +6,7 @@ mod event_handler;
 mod flex;
 mod label;
 mod line_box;
+mod new;
 mod stack;
 mod svg;
 mod uniform_list;
@@ -19,66 +20,33 @@ pub use event_handler::*;
 pub use flex::*;
 pub use label::*;
 pub use line_box::*;
+pub use new::*;
 pub use stack::*;
 pub use svg::*;
 pub use uniform_list::*;
 
 use crate::{
-    AfterLayoutContext, AppContext, Event, EventContext, LayoutContext, MutableAppContext,
-    PaintContext, SizeConstraint,
+    AfterLayoutContext, AppContext, Event, EventContext, LayoutContext, PaintContext,
+    SizeConstraint,
 };
-use pathfinder_geometry::{rect::RectF, vector::Vector2F};
-use std::any::Any;
 
-pub trait Element {
-    fn layout(
-        &mut self,
-        constraint: SizeConstraint,
-        ctx: &mut LayoutContext,
-        app: &AppContext,
-    ) -> Vector2F;
-
-    fn after_layout(&mut self, _: &mut AfterLayoutContext, _: &mut MutableAppContext) {}
-
-    fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext);
-
-    fn size(&self) -> Option<Vector2F>;
-
-    fn parent_data(&self) -> Option<&dyn Any> {
-        None
-    }
-
-    fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool;
-
-    fn boxed(self) -> Box<dyn Element>
-    where
-        Self: 'static + Sized,
-    {
-        Box::new(self)
-    }
-}
-
-pub trait ParentElement<'a>: Extend<Box<dyn Element>> + Sized {
-    fn add_children(&mut self, children: impl IntoIterator<Item = Box<dyn Element>>) {
+pub trait ParentElement<'a>: Extend<ElementBox> + Sized {
+    fn add_children(&mut self, children: impl IntoIterator<Item = ElementBox>) {
         self.extend(children);
     }
 
-    fn add_child(&mut self, child: Box<dyn Element>) {
+    fn add_child(&mut self, child: ElementBox) {
         self.add_children(Some(child));
     }
 
-    fn with_children(mut self, children: impl IntoIterator<Item = Box<dyn Element>>) -> Self {
+    fn with_children(mut self, children: impl IntoIterator<Item = ElementBox>) -> Self {
         self.add_children(children);
         self
     }
 
-    fn with_child(self, child: Box<dyn Element>) -> Self {
+    fn with_child(self, child: ElementBox) -> Self {
         self.with_children(Some(child))
     }
 }
 
-impl<'a, T> ParentElement<'a> for T where T: Extend<Box<dyn Element>> {}
-
-pub fn try_rect(origin: Option<Vector2F>, size: Option<Vector2F>) -> Option<RectF> {
-    origin.and_then(|origin| size.map(|size| RectF::new(origin, size)))
-}
+impl<'a, T> ParentElement<'a> for T where T: Extend<ElementBox> {}

gpui/src/elements/new.rs 🔗

@@ -0,0 +1,191 @@
+use crate::{
+    geometry::{rect::RectF, vector::Vector2F},
+    AfterLayoutContext, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
+};
+use core::panic;
+use replace_with::replace_with_or_abort;
+use std::any::Any;
+
+trait AnyElement {
+    fn layout(&mut self, constraint: SizeConstraint, ctx: &mut LayoutContext) -> Vector2F;
+    fn after_layout(&mut self, _: &mut AfterLayoutContext) {}
+    fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext);
+    fn dispatch_event(&mut self, event: &Event, ctx: &mut EventContext) -> bool;
+
+    fn size(&self) -> Vector2F;
+    fn metadata(&self) -> Option<&dyn Any>;
+}
+
+pub trait Element {
+    type LayoutState;
+    type PaintState;
+
+    fn layout(
+        &mut self,
+        constraint: SizeConstraint,
+        ctx: &mut LayoutContext,
+    ) -> (Vector2F, Self::LayoutState);
+
+    fn after_layout(
+        &mut self,
+        size: Vector2F,
+        layout: &mut Self::LayoutState,
+        ctx: &mut AfterLayoutContext,
+    );
+
+    fn paint(
+        &mut self,
+        bounds: RectF,
+        layout: &mut Self::LayoutState,
+        ctx: &mut PaintContext,
+    ) -> Self::PaintState;
+
+    fn dispatch_event(
+        &mut self,
+        event: &Event,
+        bounds: RectF,
+        layout: &mut Self::LayoutState,
+        paint: &mut Self::PaintState,
+        ctx: &mut EventContext,
+    ) -> bool;
+
+    fn metadata(&self) -> Option<&dyn Any> {
+        None
+    }
+
+    fn boxed(self) -> ElementBox
+    where
+        Self: 'static + Sized,
+    {
+        ElementBox(Box::new(Lifecycle::Init { element: self }))
+    }
+}
+
+pub enum Lifecycle<T: Element> {
+    Init {
+        element: T,
+    },
+    PostLayout {
+        element: T,
+        size: Vector2F,
+        layout: T::LayoutState,
+    },
+    PostPaint {
+        element: T,
+        bounds: RectF,
+        layout: T::LayoutState,
+        paint: T::PaintState,
+    },
+}
+pub struct ElementBox(Box<dyn AnyElement>);
+
+impl<T: Element> AnyElement for Lifecycle<T> {
+    fn layout(&mut self, constraint: SizeConstraint, ctx: &mut LayoutContext) -> Vector2F {
+        let mut result = None;
+        replace_with_or_abort(self, |me| match me {
+            Lifecycle::Init { mut element }
+            | Lifecycle::PostLayout { mut element, .. }
+            | Lifecycle::PostPaint { mut element, .. } => {
+                let (size, layout) = element.layout(constraint, ctx);
+                result = Some(size);
+                Lifecycle::PostLayout {
+                    element,
+                    size,
+                    layout,
+                }
+            }
+        });
+        result.unwrap()
+    }
+
+    fn after_layout(&mut self, ctx: &mut AfterLayoutContext) {
+        if let Lifecycle::PostLayout {
+            element,
+            size,
+            layout,
+        } = self
+        {
+            element.after_layout(*size, layout, ctx);
+        } else {
+            panic!("invalid element lifecycle state");
+        }
+    }
+
+    fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext) {
+        replace_with_or_abort(self, |me| {
+            if let Lifecycle::PostLayout {
+                mut element,
+                size,
+                mut layout,
+            } = me
+            {
+                let bounds = RectF::new(origin, size);
+                let paint = element.paint(bounds, &mut layout, ctx);
+                Lifecycle::PostPaint {
+                    element,
+                    bounds,
+                    layout,
+                    paint,
+                }
+            } else {
+                panic!("invalid element lifecycle state");
+            }
+        });
+    }
+
+    fn dispatch_event(&mut self, event: &Event, ctx: &mut EventContext) -> bool {
+        if let Lifecycle::PostPaint {
+            element,
+            bounds,
+            layout,
+            paint,
+        } = self
+        {
+            element.dispatch_event(event, *bounds, layout, paint, ctx)
+        } else {
+            panic!("invalid element lifecycle state");
+        }
+    }
+
+    fn size(&self) -> Vector2F {
+        match self {
+            Lifecycle::Init { .. } => panic!("invalid element lifecycle state"),
+            Lifecycle::PostLayout { size, .. } => *size,
+            Lifecycle::PostPaint { bounds, .. } => bounds.size(),
+        }
+    }
+
+    fn metadata(&self) -> Option<&dyn Any> {
+        match self {
+            Lifecycle::Init { element }
+            | Lifecycle::PostLayout { element, .. }
+            | Lifecycle::PostPaint { element, .. } => element.metadata(),
+        }
+    }
+}
+
+impl ElementBox {
+    pub fn layout(&mut self, constraint: SizeConstraint, ctx: &mut LayoutContext) -> Vector2F {
+        self.0.layout(constraint, ctx)
+    }
+
+    pub fn after_layout(&mut self, ctx: &mut AfterLayoutContext) {
+        self.0.after_layout(ctx);
+    }
+
+    pub fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext) {
+        self.0.paint(origin, ctx);
+    }
+
+    pub fn dispatch_event(&mut self, event: &Event, ctx: &mut EventContext) -> bool {
+        self.0.dispatch_event(event, ctx)
+    }
+
+    pub fn size(&self) -> Vector2F {
+        self.0.size()
+    }
+
+    pub fn metadata(&self) -> Option<&dyn Any> {
+        self.0.metadata()
+    }
+}

gpui/src/elements/stack.rs 🔗

@@ -1,65 +1,77 @@
 use crate::{
-    geometry::vector::Vector2F, AfterLayoutContext, AppContext, Element, Event, EventContext,
-    LayoutContext, MutableAppContext, PaintContext, SizeConstraint,
+    geometry::vector::Vector2F, AfterLayoutContext, Element, ElementBox, Event, EventContext,
+    LayoutContext, PaintContext, SizeConstraint,
 };
 
 pub struct Stack {
-    children: Vec<Box<dyn Element>>,
-    size: Option<Vector2F>,
+    children: Vec<ElementBox>,
 }
 
 impl Stack {
     pub fn new() -> Self {
         Stack {
             children: Vec::new(),
-            size: None,
         }
     }
 }
 
 impl Element for Stack {
+    type LayoutState = ();
+    type PaintState = ();
+
     fn layout(
         &mut self,
         constraint: SizeConstraint,
         ctx: &mut LayoutContext,
-        app: &AppContext,
-    ) -> Vector2F {
+    ) -> (Vector2F, Self::LayoutState) {
         let mut size = constraint.min;
         for child in &mut self.children {
-            size = size.max(child.layout(constraint, ctx, app));
+            size = size.max(child.layout(constraint, ctx));
         }
-        self.size = Some(size);
-        size
+        (size, ())
     }
 
-    fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &mut MutableAppContext) {
+    fn after_layout(
+        &mut self,
+        _: Vector2F,
+        _: &mut Self::LayoutState,
+        ctx: &mut AfterLayoutContext,
+    ) {
         for child in &mut self.children {
-            child.after_layout(ctx, app);
+            child.after_layout(ctx);
         }
     }
 
-    fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) {
+    fn paint(
+        &mut self,
+        bounds: pathfinder_geometry::rect::RectF,
+        _: &mut Self::LayoutState,
+        ctx: &mut PaintContext,
+    ) -> Self::PaintState {
         for child in &mut self.children {
-            child.paint(origin, ctx, app);
+            child.paint(bounds.origin(), ctx);
         }
     }
 
-    fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool {
-        for child in self.children.iter().rev() {
-            if child.dispatch_event(event, ctx, app) {
+    fn dispatch_event(
+        &mut self,
+        event: &Event,
+        _: pathfinder_geometry::rect::RectF,
+        _: &mut Self::LayoutState,
+        _: &mut Self::PaintState,
+        ctx: &mut EventContext,
+    ) -> bool {
+        for child in self.children.iter_mut().rev() {
+            if child.dispatch_event(event, ctx) {
                 return true;
             }
         }
         false
     }
-
-    fn size(&self) -> Option<Vector2F> {
-        self.size
-    }
 }
 
-impl Extend<Box<dyn Element>> for Stack {
-    fn extend<T: IntoIterator<Item = Box<dyn Element>>>(&mut self, children: T) {
+impl Extend<ElementBox> for Stack {
+    fn extend<T: IntoIterator<Item = ElementBox>>(&mut self, children: T) {
         self.children.extend(children)
     }
 }

gpui/src/elements/svg.rs 🔗

@@ -1,31 +1,27 @@
 use crate::{
-    geometry::vector::Vector2F, AfterLayoutContext, AppContext, Element, Event, EventContext,
-    LayoutContext, MutableAppContext, PaintContext, SizeConstraint,
+    geometry::vector::Vector2F, AfterLayoutContext, Element, Event, EventContext, LayoutContext,
+    PaintContext, SizeConstraint,
 };
 
 pub struct Svg {
     path: String,
-    // tree: Option<Rc<usvg::Tree>>,
-    size: Option<Vector2F>,
 }
 
 impl Svg {
     pub fn new(path: String) -> Self {
-        Self {
-            path,
-            // tree: None,
-            size: None,
-        }
+        Self { path }
     }
 }
 
 impl Element for Svg {
+    type LayoutState = ();
+    type PaintState = ();
+
     fn layout(
         &mut self,
-        constraint: SizeConstraint,
-        ctx: &mut LayoutContext,
-        _: &AppContext,
-    ) -> Vector2F {
+        _: SizeConstraint,
+        _: &mut LayoutContext,
+    ) -> (Vector2F, Self::LayoutState) {
         // let size;
         // match ctx.asset_cache.svg(&self.path) {
         //     Ok(tree) => {
@@ -52,25 +48,30 @@ impl Element for Svg {
         //     }
         // };
 
-        // self.size = Some(size);
         // size
+
         todo!()
     }
 
-    fn after_layout(&mut self, _: &mut AfterLayoutContext, _: &mut MutableAppContext) {}
-
-    fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, _: &AppContext) {
-        // if let Some(tree) = self.tree.as_ref() {
-        //     ctx.canvas
-        //         .draw_svg(tree, RectF::new(origin, self.size.unwrap()));
-        // }
+    fn after_layout(&mut self, _: Vector2F, _: &mut Self::LayoutState, _: &mut AfterLayoutContext) {
     }
 
-    fn size(&self) -> Option<Vector2F> {
-        self.size
+    fn paint(
+        &mut self,
+        _: pathfinder_geometry::rect::RectF,
+        _: &mut Self::LayoutState,
+        _: &mut PaintContext,
+    ) -> Self::PaintState {
     }
 
-    fn dispatch_event(&self, _: &Event, _: &mut EventContext, _: &AppContext) -> bool {
+    fn dispatch_event(
+        &mut self,
+        _: &Event,
+        _: pathfinder_geometry::rect::RectF,
+        _: &mut Self::LayoutState,
+        _: &mut Self::PaintState,
+        _: &mut EventContext,
+    ) -> bool {
         false
     }
 }

gpui/src/elements/uniform_list.rs 🔗

@@ -1,10 +1,13 @@
 use super::{
-    try_rect, AfterLayoutContext, AppContext, Element, Event, EventContext, LayoutContext,
-    MutableAppContext, PaintContext, SizeConstraint,
+    AfterLayoutContext, AppContext, Element, Event, EventContext, LayoutContext, PaintContext,
+    SizeConstraint,
 };
-use crate::geometry::{
-    rect::RectF,
-    vector::{vec2f, Vector2F},
+use crate::{
+    geometry::{
+        rect::RectF,
+        vector::{vec2f, Vector2F},
+    },
+    ElementBox,
 };
 use parking_lot::Mutex;
 use std::{cmp, ops::Range, sync::Arc};
@@ -12,11 +15,6 @@ use std::{cmp, ops::Range, sync::Arc};
 #[derive(Clone)]
 pub struct UniformListState(Arc<Mutex<StateInner>>);
 
-struct StateInner {
-    scroll_top: f32,
-    scroll_to: Option<usize>,
-}
-
 impl UniformListState {
     pub fn new() -> Self {
         Self(Arc::new(Mutex::new(StateInner {
@@ -28,34 +26,41 @@ impl UniformListState {
     pub fn scroll_to(&self, item_ix: usize) {
         self.0.lock().scroll_to = Some(item_ix);
     }
+
+    pub fn scroll_top(&self) -> f32 {
+        self.0.lock().scroll_top
+    }
+}
+
+struct StateInner {
+    scroll_top: f32,
+    scroll_to: Option<usize>,
+}
+
+pub struct LayoutState {
+    scroll_max: f32,
+    item_height: f32,
+    items: Vec<ElementBox>,
 }
 
 pub struct UniformList<F>
 where
-    F: Fn(Range<usize>, &mut Vec<Box<dyn Element>>, &AppContext),
+    F: Fn(Range<usize>, &mut Vec<ElementBox>, &AppContext),
 {
     state: UniformListState,
     item_count: usize,
     append_items: F,
-    scroll_max: Option<f32>,
-    items: Vec<Box<dyn Element>>,
-    origin: Option<Vector2F>,
-    size: Option<Vector2F>,
 }
 
 impl<F> UniformList<F>
 where
-    F: Fn(Range<usize>, &mut Vec<Box<dyn Element>>, &AppContext),
+    F: Fn(Range<usize>, &mut Vec<ElementBox>, &AppContext),
 {
     pub fn new(state: UniformListState, item_count: usize, build_items: F) -> Self {
         Self {
             state,
             item_count,
             append_items: build_items,
-            scroll_max: None,
-            items: Default::default(),
-            origin: None,
-            size: None,
         }
     }
 
@@ -64,8 +69,8 @@ where
         position: Vector2F,
         delta: Vector2F,
         precise: bool,
+        scroll_max: f32,
         ctx: &mut EventContext,
-        _: &AppContext,
     ) -> bool {
         if !self.rect().unwrap().contains_point(position) {
             return false;
@@ -76,18 +81,15 @@ where
         }
 
         let mut state = self.state.0.lock();
-        state.scroll_top = (state.scroll_top - delta.y())
-            .max(0.0)
-            .min(self.scroll_max.unwrap());
+        state.scroll_top = (state.scroll_top - delta.y()).max(0.0).min(scroll_max);
         ctx.dispatch_action("uniform_list:scroll", state.scroll_top);
 
         true
     }
 
-    fn autoscroll(&mut self, list_height: f32, item_height: f32) {
+    fn autoscroll(&mut self, scroll_max: f32, list_height: f32, item_height: f32) {
         let mut state = self.state.0.lock();
 
-        let scroll_max = self.item_count as f32 * item_height - list_height;
         if state.scroll_top > scroll_max {
             state.scroll_top = scroll_max;
         }
@@ -109,20 +111,23 @@ where
     }
 
     fn rect(&self) -> Option<RectF> {
-        try_rect(self.origin, self.size)
+        todo!()
+        // try_rect(self.origin, self.size)
     }
 }
 
 impl<F> Element for UniformList<F>
 where
-    F: Fn(Range<usize>, &mut Vec<Box<dyn Element>>, &AppContext),
+    F: Fn(Range<usize>, &mut Vec<ElementBox>, &AppContext),
 {
+    type LayoutState = LayoutState;
+    type PaintState = ();
+
     fn layout(
         &mut self,
         constraint: SizeConstraint,
         ctx: &mut LayoutContext,
-        app: &AppContext,
-    ) -> Vector2F {
+    ) -> (Vector2F, Self::LayoutState) {
         if constraint.max.y().is_infinite() {
             unimplemented!(
                 "UniformList does not support being rendered with an unconstrained height"
@@ -131,79 +136,91 @@ where
         let mut size = constraint.max;
         let mut item_constraint =
             SizeConstraint::new(vec2f(size.x(), 0.0), vec2f(size.x(), f32::INFINITY));
+        let mut item_height = 0.;
+        let mut scroll_max = 0.;
 
-        self.items.clear();
-        (self.append_items)(0..1, &mut self.items, app);
-        if let Some(first_item) = self.items.first_mut() {
-            let mut item_size = first_item.layout(item_constraint, ctx, app);
+        let mut items = Vec::new();
+        (self.append_items)(0..1, &mut items, ctx.app);
+        if let Some(first_item) = items.first_mut() {
+            let mut item_size = first_item.layout(item_constraint, ctx);
             item_size.set_x(size.x());
             item_constraint.min = item_size;
             item_constraint.max = item_size;
+            item_height = item_size.y();
 
-            let scroll_height = self.item_count as f32 * item_size.y();
+            let scroll_height = self.item_count as f32 * item_height;
             if scroll_height < size.y() {
                 size.set_y(size.y().min(scroll_height).max(constraint.min.y()));
             }
 
-            self.autoscroll(size.y(), item_size.y());
+            scroll_max = item_height * self.item_count as f32 - size.y();
+            self.autoscroll(scroll_max, size.y(), item_height);
 
-            let start = cmp::min(
-                (self.scroll_top() / item_size.y()) as usize,
-                self.item_count,
-            );
+            items.clear();
+            let start = cmp::min((self.scroll_top() / item_height) as usize, self.item_count);
             let end = cmp::min(
                 self.item_count,
-                start + (size.y() / item_size.y()).ceil() as usize + 1,
+                start + (size.y() / item_height).ceil() as usize + 1,
             );
-            self.items.clear();
-            (self.append_items)(start..end, &mut self.items, app);
-
-            self.scroll_max = Some(item_size.y() * self.item_count as f32 - size.y());
-
-            for item in &mut self.items {
-                item.layout(item_constraint, ctx, app);
+            (self.append_items)(start..end, &mut items, ctx.app);
+            for item in &mut items {
+                item.layout(item_constraint, ctx);
             }
         }
 
-        self.size = Some(size);
-        size
+        (
+            size,
+            LayoutState {
+                item_height,
+                scroll_max,
+                items,
+            },
+        )
     }
 
-    fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &mut MutableAppContext) {
-        for item in &mut self.items {
-            item.after_layout(ctx, app);
+    fn after_layout(
+        &mut self,
+        _: Vector2F,
+        layout: &mut Self::LayoutState,
+        ctx: &mut AfterLayoutContext,
+    ) {
+        for item in &mut layout.items {
+            item.after_layout(ctx);
         }
     }
 
-    fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) {
-        // self.origin = Some(origin);
-
-        // if let Some(item) = self.items.first() {
-        //     ctx.canvas.save();
-        //     let mut clip_path = Path2D::new();
-        //     clip_path.rect(RectF::new(origin, self.size.unwrap()));
-        //     ctx.canvas.clip_path(clip_path, FillRule::Winding);
-
-        //     let item_height = item.size().unwrap().y();
-        //     let mut item_origin = origin - vec2f(0.0, self.state.0.lock().scroll_top % item_height);
-        //     for item in &mut self.items {
-        //         item.paint(item_origin, ctx, app);
-        //         item_origin += vec2f(0.0, item_height);
-        //     }
-        //     ctx.canvas.restore();
-        // }
-    }
-
-    fn size(&self) -> Option<Vector2F> {
-        self.size
+    fn paint(
+        &mut self,
+        bounds: RectF,
+        layout: &mut Self::LayoutState,
+        ctx: &mut PaintContext,
+    ) -> Self::PaintState {
+        // ctx.canvas.save();
+        // let mut clip_path = Path2D::new();
+        // clip_path.rect(RectF::new(origin, self.size.unwrap()));
+        // ctx.canvas.clip_path(clip_path, FillRule::Winding);
+
+        let mut item_origin =
+            bounds.origin() - vec2f(0.0, self.state.scroll_top() % layout.item_height);
+
+        for item in &mut layout.items {
+            item.paint(item_origin, ctx);
+            item_origin += vec2f(0.0, layout.item_height);
+        }
+        // ctx.canvas.restore();
     }
 
-    fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool {
+    fn dispatch_event(
+        &mut self,
+        event: &Event,
+        _: RectF,
+        layout: &mut Self::LayoutState,
+        _: &mut Self::PaintState,
+        ctx: &mut EventContext,
+    ) -> bool {
         let mut handled = false;
-        for item in &self.items {
-            if item.dispatch_event(event, ctx, app) {
-                handled = true;
-            }
+        for item in &mut layout.items {
+            handled = item.dispatch_event(event, ctx) || handled;
         }
 
         match event {
@@ -212,7 +229,7 @@ where
                 delta,
                 precise,
             } => {
-                if self.scroll(*position, *delta, *precise, ctx, app) {
+                if self.scroll(*position, *delta, *precise, layout.scroll_max, ctx) {
                     handled = true;
                 }
             }

gpui/src/lib.rs 🔗

@@ -11,7 +11,7 @@ pub use scene::{Border, Scene};
 pub mod text_layout;
 pub use text_layout::TextLayoutCache;
 mod util;
-pub use elements::Element;
+pub use elements::{Element, ElementBox};
 pub mod executor;
 pub mod keymap;
 pub mod platform;

gpui/src/platform/mac/renderer.rs 🔗

@@ -97,7 +97,6 @@ impl Renderer {
         for quad_batch in layer.quads().chunks(batch_size) {
             for (ix, quad) in quad_batch.iter().enumerate() {
                 let bounds = quad.bounds * scene.scale_factor();
-                log::info!("render quad {:?}", quad);
                 let shader_quad = shaders::GPUIQuad {
                     origin: bounds.origin().to_float2(),
                     size: bounds.size().to_float2(),

gpui/src/platform/mac/window.rs 🔗

@@ -292,7 +292,6 @@ impl platform::WindowContext for WindowState {
     }
 
     fn present_scene(&mut self, scene: Scene) {
-        log::info!("present scene");
         self.scene_to_render = Some(scene);
         unsafe {
             let _: () = msg_send![self.native_window.contentView(), setNeedsDisplay: YES];
@@ -421,7 +420,6 @@ extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
 }
 
 extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
-    log::info!("display layer");
     unsafe {
         let window_state = get_window_state(this);
         let mut window_state = window_state.as_ref().borrow_mut();

gpui/src/presenter.rs 🔗

@@ -4,14 +4,14 @@ use crate::{
     fonts::FontCache,
     platform::Event,
     text_layout::TextLayoutCache,
-    AssetCache, Scene,
+    AssetCache, ElementBox, Scene,
 };
 use pathfinder_geometry::vector::{vec2f, Vector2F};
 use std::{any::Any, collections::HashMap, sync::Arc};
 
 pub struct Presenter {
     window_id: usize,
-    rendered_views: HashMap<usize, Box<dyn Element>>,
+    rendered_views: HashMap<usize, ElementBox>,
     parents: HashMap<usize, usize>,
     font_cache: Arc<FontCache>,
     text_layout_cache: TextLayoutCache,
@@ -68,13 +68,14 @@ impl Presenter {
         if let Some(root_view_id) = app.root_view_id(self.window_id) {
             self.layout(window_size, app.downgrade());
             self.after_layout(app);
-            let mut paint_ctx = PaintContext {
+            let mut ctx = PaintContext {
                 scene: &mut scene,
                 font_cache: &self.font_cache,
                 text_layout_cache: &self.text_layout_cache,
                 rendered_views: &mut self.rendered_views,
+                app: app.downgrade(),
             };
-            paint_ctx.paint(root_view_id, Vector2F::zero(), app.downgrade());
+            ctx.paint(root_view_id, Vector2F::zero());
             self.text_layout_cache.finish_frame();
         } else {
             log::error!("could not find root_view_id for window {}", self.window_id);
@@ -92,8 +93,9 @@ impl Presenter {
                 text_layout_cache: &self.text_layout_cache,
                 asset_cache: &self.asset_cache,
                 view_stack: Vec::new(),
+                app,
             };
-            layout_ctx.layout(root_view_id, SizeConstraint::strict(size), app);
+            layout_ctx.layout(root_view_id, SizeConstraint::strict(size));
         }
     }
 
@@ -103,35 +105,27 @@ impl Presenter {
                 rendered_views: &mut self.rendered_views,
                 font_cache: &self.font_cache,
                 text_layout_cache: &self.text_layout_cache,
+                app,
             };
-            ctx.after_layout(root_view_id, app);
+            ctx.after_layout(root_view_id);
         }
     }
 
-    pub fn responder_chain(&self, app: &AppContext) -> Option<Vec<usize>> {
-        app.focused_view_id(self.window_id).map(|mut view_id| {
-            let mut chain = vec![view_id];
-            while let Some(parent_id) = self.parents.get(&view_id) {
-                view_id = *parent_id;
-                chain.push(view_id);
-            }
-            chain.reverse();
-            chain
-        })
-    }
-
-    pub fn dispatch_event(&self, event: Event, app: &AppContext) -> Vec<ActionToDispatch> {
-        let mut event_ctx = EventContext {
-            rendered_views: &self.rendered_views,
-            actions: Vec::new(),
-            font_cache: &self.font_cache,
-            text_layout_cache: &self.text_layout_cache,
-            view_stack: Vec::new(),
-        };
+    pub fn dispatch_event(&mut self, event: Event, app: &AppContext) -> Vec<ActionToDispatch> {
         if let Some(root_view_id) = app.root_view_id(self.window_id) {
-            event_ctx.dispatch_event_on_view(root_view_id, &event, app);
+            let mut ctx = EventContext {
+                rendered_views: &mut self.rendered_views,
+                actions: Vec::new(),
+                font_cache: &self.font_cache,
+                text_layout_cache: &self.text_layout_cache,
+                view_stack: Vec::new(),
+                app,
+            };
+            ctx.dispatch_event(root_view_id, &event);
+            ctx.actions
+        } else {
+            Vec::new()
         }
-        event_ctx.actions
     }
 }
 
@@ -142,22 +136,23 @@ pub struct ActionToDispatch {
 }
 
 pub struct LayoutContext<'a> {
-    rendered_views: &'a mut HashMap<usize, Box<dyn Element>>,
+    rendered_views: &'a mut HashMap<usize, ElementBox>,
     parents: &'a mut HashMap<usize, usize>,
     pub font_cache: &'a FontCache,
     pub text_layout_cache: &'a TextLayoutCache,
     pub asset_cache: &'a AssetCache,
+    pub app: &'a AppContext,
     view_stack: Vec<usize>,
 }
 
 impl<'a> LayoutContext<'a> {
-    fn layout(&mut self, view_id: usize, constraint: SizeConstraint, app: &AppContext) -> Vector2F {
+    fn layout(&mut self, view_id: usize, constraint: SizeConstraint) -> Vector2F {
         if let Some(parent_id) = self.view_stack.last() {
             self.parents.insert(view_id, *parent_id);
         }
         self.view_stack.push(view_id);
         let mut rendered_view = self.rendered_views.remove(&view_id).unwrap();
-        let size = rendered_view.layout(constraint, self, app);
+        let size = rendered_view.layout(constraint, self);
         self.rendered_views.insert(view_id, rendered_view);
         self.view_stack.pop();
         size
@@ -165,55 +160,54 @@ impl<'a> LayoutContext<'a> {
 }
 
 pub struct AfterLayoutContext<'a> {
-    rendered_views: &'a mut HashMap<usize, Box<dyn Element>>,
+    rendered_views: &'a mut HashMap<usize, ElementBox>,
     pub font_cache: &'a FontCache,
     pub text_layout_cache: &'a TextLayoutCache,
+    pub app: &'a mut MutableAppContext,
 }
 
 impl<'a> AfterLayoutContext<'a> {
-    fn after_layout(&mut self, view_id: usize, app: &mut MutableAppContext) {
+    fn after_layout(&mut self, view_id: usize) {
         if let Some(mut view) = self.rendered_views.remove(&view_id) {
-            view.after_layout(self, app);
+            view.after_layout(self);
             self.rendered_views.insert(view_id, view);
         }
     }
 }
 
 pub struct PaintContext<'a> {
-    rendered_views: &'a mut HashMap<usize, Box<dyn Element>>,
+    rendered_views: &'a mut HashMap<usize, ElementBox>,
     pub scene: &'a mut Scene,
     pub font_cache: &'a FontCache,
     pub text_layout_cache: &'a TextLayoutCache,
+    pub app: &'a AppContext,
 }
 
 impl<'a> PaintContext<'a> {
-    fn paint(&mut self, view_id: usize, origin: Vector2F, app: &AppContext) {
+    fn paint(&mut self, view_id: usize, origin: Vector2F) {
         if let Some(mut tree) = self.rendered_views.remove(&view_id) {
-            tree.paint(origin, self, app);
+            tree.paint(origin, self);
             self.rendered_views.insert(view_id, tree);
         }
     }
 }
 
 pub struct EventContext<'a> {
-    rendered_views: &'a HashMap<usize, Box<dyn Element>>,
+    rendered_views: &'a mut HashMap<usize, ElementBox>,
     actions: Vec<ActionToDispatch>,
     pub font_cache: &'a FontCache,
     pub text_layout_cache: &'a TextLayoutCache,
+    pub app: &'a AppContext,
     view_stack: Vec<usize>,
 }
 
 impl<'a> EventContext<'a> {
-    pub fn dispatch_event_on_view(
-        &mut self,
-        view_id: usize,
-        event: &Event,
-        app: &AppContext,
-    ) -> bool {
-        if let Some(element) = self.rendered_views.get(&view_id) {
+    fn dispatch_event(&mut self, view_id: usize, event: &Event) -> bool {
+        if let Some(mut element) = self.rendered_views.remove(&view_id) {
             self.view_stack.push(view_id);
-            let result = element.dispatch_event(event, self, app);
+            let result = element.dispatch_event(event, self);
             self.view_stack.pop();
+            self.rendered_views.insert(view_id, element);
             result
         } else {
             false
@@ -298,47 +292,54 @@ impl SizeConstraint {
 
 pub struct ChildView {
     view_id: usize,
-    size: Option<Vector2F>,
-    origin: Option<Vector2F>,
 }
 
 impl ChildView {
     pub fn new(view_id: usize) -> Self {
-        Self {
-            view_id,
-            size: None,
-            origin: None,
-        }
+        Self { view_id }
     }
 }
 
 impl Element for ChildView {
+    type LayoutState = ();
+    type PaintState = ();
+
     fn layout(
         &mut self,
         constraint: SizeConstraint,
         ctx: &mut LayoutContext,
-        app: &AppContext,
-    ) -> Vector2F {
-        let size = ctx.layout(self.view_id, constraint, app);
-        self.size = Some(size);
-        size
-    }
-
-    fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &mut MutableAppContext) {
-        ctx.after_layout(self.view_id, app);
+    ) -> (Vector2F, Self::LayoutState) {
+        let size = ctx.layout(self.view_id, constraint);
+        (size, ())
     }
 
-    fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) {
-        self.origin = Some(origin);
-        ctx.paint(self.view_id, origin, app);
+    fn after_layout(
+        &mut self,
+        _: Vector2F,
+        _: &mut Self::LayoutState,
+        ctx: &mut AfterLayoutContext,
+    ) {
+        ctx.after_layout(self.view_id);
     }
 
-    fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool {
-        ctx.dispatch_event_on_view(self.view_id, event, app)
+    fn paint(
+        &mut self,
+        bounds: pathfinder_geometry::rect::RectF,
+        _: &mut Self::LayoutState,
+        ctx: &mut PaintContext,
+    ) -> Self::PaintState {
+        ctx.paint(self.view_id, bounds.origin());
     }
 
-    fn size(&self) -> Option<Vector2F> {
-        self.size
+    fn dispatch_event(
+        &mut self,
+        event: &Event,
+        _: pathfinder_geometry::rect::RectF,
+        _: &mut Self::LayoutState,
+        _: &mut Self::PaintState,
+        ctx: &mut EventContext,
+    ) -> bool {
+        ctx.dispatch_event(self.view_id, event)
     }
 }
 

gpui/src/text_layout.rs 🔗

@@ -2,7 +2,7 @@ use crate::{
     color::ColorU,
     fonts::{FontCache, FontId, GlyphId},
     geometry::rect::RectF,
-    scene::Scene,
+    PaintContext,
 };
 use core_foundation::{
     attributed_string::CFMutableAttributedString,
@@ -188,11 +188,9 @@ impl Line {
 
     pub fn paint(
         &self,
-        _origin: Vector2F,
-        _viewport_rect: RectF,
+        _bounds: RectF,
         _colors: &[(Range<usize>, ColorU)],
-        _scene: Scene,
-        _font_cache: &FontCache,
+        _ctx: &mut PaintContext,
     ) {
         // canvas.set_font_size(self.font_size);
         // let mut colors = colors.iter().peekable();

zed/src/editor/buffer_element.rs 🔗

@@ -15,31 +15,25 @@ use std::{
 
 pub struct BufferElement {
     view: ViewHandle<BufferView>,
-    layout: Option<LayoutState>,
-    paint: Option<PaintState>,
 }
 
 impl BufferElement {
     pub fn new(view: ViewHandle<BufferView>) -> Self {
-        Self {
-            view,
-            layout: None,
-            paint: None,
-        }
+        Self { view }
     }
 
     fn mouse_down(
         &self,
         position: Vector2F,
         cmd: bool,
+        layout: &mut LayoutState,
+        paint: &mut PaintState,
         ctx: &mut EventContext,
-        app: &AppContext,
     ) -> bool {
-        let layout = self.layout.as_ref().unwrap();
-        let paint = self.paint.as_ref().unwrap();
-        if paint.text_rect.contains_point(position) {
-            let view = self.view.as_ref(app);
-            let position = paint.point_for_position(view, layout, position, ctx.font_cache, app);
+        if paint.text_bounds.contains_point(position) {
+            let view = self.view.as_ref(ctx.app);
+            let position =
+                paint.point_for_position(view, layout, position, ctx.font_cache, ctx.app);
             ctx.dispatch_action("buffer:select", SelectAction::Begin { position, add: cmd });
             true
         } else {
@@ -47,8 +41,8 @@ impl BufferElement {
         }
     }
 
-    fn mouse_up(&self, _position: Vector2F, ctx: &mut EventContext, app: &AppContext) -> bool {
-        if self.view.as_ref(app).is_selecting() {
+    fn mouse_up(&self, _position: Vector2F, ctx: &mut EventContext) -> bool {
+        if self.view.as_ref(ctx.app).is_selecting() {
             ctx.dispatch_action("buffer:select", SelectAction::End);
             true
         } else {
@@ -56,13 +50,17 @@ impl BufferElement {
         }
     }
 
-    fn mouse_dragged(&self, position: Vector2F, ctx: &mut EventContext, app: &AppContext) -> bool {
-        let view = self.view.as_ref(app);
-        let layout = self.layout.as_ref().unwrap();
-        let paint = self.paint.as_ref().unwrap();
+    fn mouse_dragged(
+        &self,
+        position: Vector2F,
+        layout: &mut LayoutState,
+        paint: &mut PaintState,
+        ctx: &mut EventContext,
+    ) -> bool {
+        let view = self.view.as_ref(ctx.app);
 
         if view.is_selecting() {
-            let rect = self.paint.as_ref().unwrap().text_rect;
+            let rect = paint.text_bounds;
             let mut scroll_delta = Vector2F::zero();
 
             let vertical_margin = view.line_height(ctx.font_cache).min(rect.height() / 3.0);
@@ -92,15 +90,16 @@ impl BufferElement {
             ctx.dispatch_action(
                 "buffer:select",
                 SelectAction::Update {
-                    position: paint.point_for_position(view, layout, position, ctx.font_cache, app),
+                    position: paint.point_for_position(
+                        view,
+                        layout,
+                        position,
+                        ctx.font_cache,
+                        ctx.app,
+                    ),
                     scroll_position: (view.scroll_position() + scroll_delta).clamp(
                         Vector2F::zero(),
-                        self.layout.as_ref().unwrap().scroll_max(
-                            view,
-                            ctx.font_cache,
-                            ctx.text_layout_cache,
-                            app,
-                        ),
+                        layout.scroll_max(view, ctx.font_cache, ctx.text_layout_cache, ctx.app),
                     ),
                 },
             );
@@ -110,8 +109,8 @@ impl BufferElement {
         }
     }
 
-    fn key_down(&self, chars: &str, ctx: &mut EventContext, app: &AppContext) -> bool {
-        if self.view.is_focused(app) {
+    fn key_down(&self, chars: &str, ctx: &mut EventContext) -> bool {
+        if self.view.is_focused(ctx.app) {
             if chars.is_empty() {
                 false
             } else {
@@ -132,12 +131,11 @@ impl BufferElement {
         position: Vector2F,
         delta: Vector2F,
         precise: bool,
+        layout: &mut LayoutState,
+        paint: &mut PaintState,
         ctx: &mut EventContext,
-        app: &AppContext,
     ) -> bool {
-        let paint = self.paint.as_ref().unwrap();
-
-        if !paint.rect.contains_point(position) {
+        if !paint.bounds.contains_point(position) {
             return false;
         }
 
@@ -145,7 +143,7 @@ impl BufferElement {
             todo!("still need to handle non-precise scroll events from a mouse wheel");
         }
 
-        let view = self.view.as_ref(app);
+        let view = self.view.as_ref(ctx.app);
         let font_cache = &ctx.font_cache;
         let layout_cache = &ctx.text_layout_cache;
         let max_glyph_width = view.em_width(font_cache);
@@ -155,10 +153,7 @@ impl BufferElement {
         let y = (view.scroll_position().y() * line_height - delta.y()) / line_height;
         let scroll_position = vec2f(x, y).clamp(
             Vector2F::zero(),
-            self.layout
-                .as_ref()
-                .unwrap()
-                .scroll_max(view, font_cache, layout_cache, app),
+            layout.scroll_max(view, font_cache, layout_cache, ctx.app),
         );
 
         ctx.dispatch_action("buffer:scroll", scroll_position);
@@ -166,7 +161,7 @@ impl BufferElement {
         true
     }
 
-    fn paint_gutter(&mut self, rect: RectF, ctx: &mut PaintContext, app: &AppContext) {
+    fn paint_gutter(&mut self, rect: RectF, ctx: &mut PaintContext) {
         // if let Some(layout) = self.layout.as_ref() {
         //     let view = self.view.as_ref(app);
         //     let scene = &mut ctx.scene;
@@ -202,7 +197,7 @@ impl BufferElement {
         // }
     }
 
-    fn paint_text(&mut self, rect: RectF, ctx: &mut PaintContext, app: &AppContext) {
+    fn paint_text(&mut self, rect: RectF, ctx: &mut PaintContext) {
         // if let Some(layout) = self.layout.as_ref() {
         //     let scene = &mut ctx.scene;
         //     let font_cache = &ctx.font_cache;
@@ -323,12 +318,15 @@ impl BufferElement {
 }
 
 impl Element for BufferElement {
+    type LayoutState = Option<LayoutState>;
+    type PaintState = Option<PaintState>;
+
     fn layout(
         &mut self,
         constraint: SizeConstraint,
         ctx: &mut LayoutContext,
-        app: &AppContext,
-    ) -> Vector2F {
+    ) -> (Vector2F, Self::LayoutState) {
+        let app = ctx.app;
         let mut size = constraint.max;
         if size.y().is_infinite() {
             let view = self.view.as_ref(app);
@@ -350,7 +348,7 @@ impl Element for BufferElement {
             match view.max_line_number_width(ctx.font_cache, ctx.text_layout_cache, app) {
                 Err(error) => {
                     log::error!("error computing max line number width: {}", error);
-                    return size;
+                    return (size, None);
                 }
                 Ok(width) => gutter_width = width + gutter_padding * 2.0,
             }
@@ -368,7 +366,7 @@ impl Element for BufferElement {
             match view.layout_line_numbers(size.y(), ctx.font_cache, ctx.text_layout_cache, app) {
                 Err(error) => {
                     log::error!("error laying out line numbers: {}", error);
-                    return size;
+                    return (size, None);
                 }
                 Ok(layouts) => layouts,
             }
@@ -385,7 +383,7 @@ impl Element for BufferElement {
             match view.layout_lines(start_row..end_row, font_cache, layout_cache, app) {
                 Err(error) => {
                     log::error!("error laying out lines: {}", error);
-                    return size;
+                    return (size, None);
                 }
                 Ok(layouts) => {
                     for line in &layouts {
@@ -398,84 +396,108 @@ impl Element for BufferElement {
                 }
             };
 
-        self.layout = Some(LayoutState {
+        (
             size,
-            gutter_size,
-            gutter_padding,
-            text_size,
-            line_layouts,
-            line_number_layouts,
-            max_visible_line_width,
-            autoscroll_horizontally,
-        });
-
-        size
+            Some(LayoutState {
+                size,
+                gutter_size,
+                gutter_padding,
+                text_size,
+                line_layouts,
+                line_number_layouts,
+                max_visible_line_width,
+                autoscroll_horizontally,
+            }),
+        )
     }
 
-    fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &mut MutableAppContext) {
-        let layout = self.layout.as_ref().unwrap();
-
-        let view = self.view.as_ref(app);
-        view.clamp_scroll_left(
-            layout
-                .scroll_max(view, ctx.font_cache, ctx.text_layout_cache, app.downgrade())
-                .x(),
-        );
+    fn after_layout(
+        &mut self,
+        _: Vector2F,
+        layout: &mut Option<LayoutState>,
+        ctx: &mut AfterLayoutContext,
+    ) {
+        if let Some(layout) = layout {
+            let app = ctx.app.downgrade();
 
-        if layout.autoscroll_horizontally {
-            view.autoscroll_horizontally(
-                view.scroll_position().y() as u32,
-                layout.text_size.x(),
-                layout.scroll_width(view, ctx.font_cache, ctx.text_layout_cache, app.downgrade()),
-                view.em_width(ctx.font_cache),
-                &layout.line_layouts,
-                app.downgrade(),
+            let view = self.view.as_ref(app);
+            view.clamp_scroll_left(
+                layout
+                    .scroll_max(view, ctx.font_cache, ctx.text_layout_cache, app)
+                    .x(),
             );
+
+            if layout.autoscroll_horizontally {
+                view.autoscroll_horizontally(
+                    view.scroll_position().y() as u32,
+                    layout.text_size.x(),
+                    layout.scroll_width(view, ctx.font_cache, ctx.text_layout_cache, app),
+                    view.em_width(ctx.font_cache),
+                    &layout.line_layouts,
+                    app,
+                );
+            }
         }
     }
 
-    fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) {
-        let rect;
-        let gutter_rect;
-        let text_rect;
-        {
-            let layout = self.layout.as_ref().unwrap();
-            rect = RectF::new(origin, layout.size);
-            gutter_rect = RectF::new(origin, layout.gutter_size);
-            text_rect = RectF::new(
-                origin + vec2f(layout.gutter_size.x(), 0.0),
+    fn paint(
+        &mut self,
+        bounds: RectF,
+        layout: &mut Self::LayoutState,
+        ctx: &mut PaintContext,
+    ) -> Self::PaintState {
+        if let Some(layout) = layout {
+            let gutter_bounds = RectF::new(bounds.origin(), layout.gutter_size);
+            let text_bounds = RectF::new(
+                bounds.origin() + vec2f(layout.gutter_size.x(), 0.0),
                 layout.text_size,
             );
-        }
 
-        if self.view.as_ref(app).is_gutter_visible() {
-            self.paint_gutter(gutter_rect, ctx, app);
-        }
-        self.paint_text(text_rect, ctx, app);
-
-        self.paint = Some(PaintState { rect, text_rect });
-    }
+            if self.view.as_ref(ctx.app).is_gutter_visible() {
+                self.paint_gutter(gutter_bounds, ctx);
+            }
+            self.paint_text(text_bounds, ctx);
 
-    fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool {
-        match event {
-            Event::LeftMouseDown { position, cmd } => self.mouse_down(*position, *cmd, ctx, app),
-            Event::LeftMouseUp { position } => self.mouse_up(*position, ctx, app),
-            Event::LeftMouseDragged { position } => self.mouse_dragged(*position, ctx, app),
-            Event::ScrollWheel {
-                position,
-                delta,
-                precise,
-            } => self.scroll(*position, *delta, *precise, ctx, app),
-            Event::KeyDown { chars, .. } => self.key_down(chars, ctx, app),
+            Some(PaintState {
+                bounds,
+                text_bounds,
+            })
+        } else {
+            None
         }
     }
 
-    fn size(&self) -> Option<Vector2F> {
-        self.layout.as_ref().map(|layout| layout.size)
+    fn dispatch_event(
+        &mut self,
+        event: &Event,
+        _: RectF,
+        layout: &mut Self::LayoutState,
+        paint: &mut Self::PaintState,
+        ctx: &mut EventContext,
+    ) -> bool {
+        if let (Some(layout), Some(paint)) = (layout, paint) {
+            match event {
+                Event::LeftMouseDown { position, cmd } => {
+                    self.mouse_down(*position, *cmd, layout, paint, ctx)
+                }
+                Event::LeftMouseUp { position } => self.mouse_up(*position, ctx),
+                Event::LeftMouseDragged { position } => {
+                    self.mouse_dragged(*position, layout, paint, ctx)
+                }
+                Event::ScrollWheel {
+                    position,
+                    delta,
+                    precise,
+                } => self.scroll(*position, *delta, *precise, layout, paint, ctx),
+                Event::KeyDown { chars, .. } => self.key_down(chars, ctx),
+            }
+        } else {
+            false
+        }
     }
 }
 
-struct LayoutState {
+pub struct LayoutState {
     size: Vector2F,
     gutter_size: Vector2F,
     gutter_padding: f32,
@@ -518,9 +540,9 @@ impl LayoutState {
     }
 }
 
-struct PaintState {
-    rect: RectF,
-    text_rect: RectF,
+pub struct PaintState {
+    bounds: RectF,
+    text_bounds: RectF,
 }
 
 impl PaintState {
@@ -533,7 +555,7 @@ impl PaintState {
         app: &AppContext,
     ) -> DisplayPoint {
         let scroll_position = view.scroll_position();
-        let position = position - self.text_rect.origin();
+        let position = position - self.text_bounds.origin();
         let y = position.y().max(0.0).min(layout.size.y());
         let row = ((y / view.line_height(font_cache)) + scroll_position.y()) as u32;
         let row = cmp::min(row, view.max_point(app).row());

zed/src/editor/buffer_view.rs 🔗

@@ -8,7 +8,8 @@ use easy_parallel::Parallel;
 use gpui::{
     fonts::{FontCache, Properties as FontProperties},
     keymap::Binding,
-    text_layout, App, AppContext, Element, Entity, ModelHandle, View, ViewContext, WeakViewHandle,
+    text_layout, App, AppContext, Element, ElementBox, Entity, ModelHandle, View, ViewContext,
+    WeakViewHandle,
 };
 use gpui::{geometry::vector::Vector2F, TextLayoutCache};
 use parking_lot::Mutex;
@@ -892,6 +893,7 @@ impl BufferView {
         font_cache.scale_metric(bounds.width(), font_id, settings.buffer_font_size)
     }
 
+    // TODO: Can we make this not return a result?
     pub fn max_line_number_width(
         &self,
         font_cache: &FontCache,
@@ -1140,7 +1142,7 @@ impl Entity for BufferView {
 }
 
 impl View for BufferView {
-    fn render<'a>(&self, app: &AppContext) -> Box<dyn Element> {
+    fn render<'a>(&self, app: &AppContext) -> ElementBox {
         BufferElement::new(self.handle.upgrade(app).unwrap()).boxed()
     }
 

zed/src/file_finder.rs 🔗

@@ -57,7 +57,7 @@ impl View for FileFinder {
         "FileFinder"
     }
 
-    fn render(&self, _: &AppContext) -> Box<dyn Element> {
+    fn render(&self, _: &AppContext) -> ElementBox {
         Align::new(
             ConstrainedBox::new(
                 Container::new(
@@ -98,7 +98,7 @@ impl View for FileFinder {
 }
 
 impl FileFinder {
-    fn render_matches(&self) -> Box<dyn Element> {
+    fn render_matches(&self) -> ElementBox {
         if self.matches.is_empty() {
             let settings = smol::block_on(self.settings.read());
             return Container::new(
@@ -140,7 +140,7 @@ impl FileFinder {
         path_match: &PathMatch,
         index: usize,
         app: &AppContext,
-    ) -> Option<Box<dyn Element>> {
+    ) -> Option<ElementBox> {
         let tree_id = path_match.tree_id;
         let entry_id = path_match.entry_id;
 
@@ -227,7 +227,7 @@ impl FileFinder {
             }
 
             EventHandler::new(container.boxed())
-                .on_mouse_down(move |ctx, _| {
+                .on_mouse_down(move |ctx| {
                     ctx.dispatch_action("file_finder:select", (tree_id, entry_id));
                     true
                 })

zed/src/workspace/pane.rs 🔗

@@ -173,7 +173,7 @@ impl Pane {
         ctx.emit(Event::Split(direction));
     }
 
-    fn render_tabs<'a>(&self, app: &AppContext) -> Box<dyn Element> {
+    fn render_tabs<'a>(&self, app: &AppContext) -> ElementBox {
         let settings = smol::block_on(self.settings.read());
         let border_color = ColorU::new(0xdb, 0xdb, 0xdc, 0xff);
 
@@ -209,7 +209,7 @@ impl Pane {
                     1.0,
                     ConstrainedBox::new(
                         EventHandler::new(container.boxed())
-                            .on_mouse_down(move |ctx, _| {
+                            .on_mouse_down(move |ctx| {
                                 ctx.dispatch_action("pane:activate_item", ix);
                                 true
                             })
@@ -253,7 +253,7 @@ impl View for Pane {
         "Pane"
     }
 
-    fn render<'a>(&self, app: &AppContext) -> Box<dyn Element> {
+    fn render<'a>(&self, app: &AppContext) -> ElementBox {
         if let Some(active_item) = self.active_item() {
             Flex::column()
                 .with_child(self.render_tabs(app))

zed/src/workspace/pane_group.rs 🔗

@@ -48,7 +48,7 @@ impl PaneGroup {
         }
     }
 
-    pub fn render<'a>(&self) -> Box<dyn Element> {
+    pub fn render<'a>(&self) -> ElementBox {
         self.root.render()
     }
 }
@@ -77,7 +77,7 @@ impl Member {
         Member::Axis(PaneAxis { axis, members })
     }
 
-    pub fn render<'a>(&self) -> Box<dyn Element> {
+    pub fn render<'a>(&self) -> ElementBox {
         match self {
             Member::Pane(view_id) => ChildView::new(*view_id).boxed(),
             Member::Axis(axis) => axis.render(),
@@ -168,7 +168,7 @@ impl PaneAxis {
         }
     }
 
-    fn render<'a>(&self) -> Box<dyn Element> {
+    fn render<'a>(&self) -> ElementBox {
         let last_member_ix = self.members.len() - 1;
         Flex::new(self.axis)
             .with_children(self.members.iter().enumerate().map(|(ix, member)| {

zed/src/workspace/workspace_view.rs 🔗

@@ -141,7 +141,6 @@ impl WorkspaceView {
             ctx.focus(&modal);
             self.modal = Some(modal.into());
         }
-        log::info!("toggle modal notify");
         ctx.notify();
     }
 
@@ -304,7 +303,7 @@ impl View for WorkspaceView {
         "Workspace"
     }
 
-    fn render(&self, _: &AppContext) -> Box<dyn Element> {
+    fn render(&self, _: &AppContext) -> ElementBox {
         Container::new(
             // self.center.render(bump)
             Stack::new()