WIP

Nathan Sobo created

Change summary

crates/gpui/src/app.rs                          | 262 ++------
crates/gpui/src/app/test_app_context.rs         |   6 
crates/gpui/src/app/window.rs                   |   1 
crates/gpui/src/elements.rs                     |  53 
crates/gpui/src/elements/canvas.rs              |  14 
crates/gpui/src/elements/container.rs           |   2 
crates/gpui/src/elements/flex.rs                |   6 
crates/gpui/src/elements/list.rs                | 547 +++++++++---------
crates/gpui/src/elements/mouse_event_handler.rs |  10 
crates/gpui/src/elements/overlay.rs             |   2 
crates/gpui/src/elements/resizable.rs           |  36 
crates/gpui/src/elements/stack.rs               |  38 
crates/gpui/src/elements/svg.rs                 |  21 
crates/gpui/src/elements/text.rs                |  27 
crates/gpui/src/elements/tooltip.rs             | 104 +-
crates/gpui/src/elements/uniform_list.rs        |   2 
crates/gpui/src/gpui.rs                         |   7 
crates/gpui/src/scene/mouse_region.rs           | 184 +++--
crates/gpui/src/test.rs                         |   6 
crates/gpui/src/text_layout.rs                  |   9 
crates/gpui/src/views/select.rs                 |  64 +
21 files changed, 691 insertions(+), 710 deletions(-)

Detailed changes

crates/gpui/src/app.rs 🔗

@@ -69,7 +69,7 @@ pub trait Entity: 'static {
 
 pub trait View: Entity + Sized {
     fn ui_name() -> &'static str;
-    fn render(&mut self, cx: &mut RenderContext<'_, Self>) -> ElementBox<Self>;
+    fn render(&mut self, cx: &mut ViewContext<'_, '_, Self>) -> ElementBox<Self>;
     fn focus_in(&mut self, _: AnyViewHandle, _: &mut ViewContext<Self>) {}
     fn focus_out(&mut self, _: AnyViewHandle, _: &mut ViewContext<Self>) {}
     fn key_down(&mut self, _: &KeyDownEvent, _: &mut ViewContext<Self>) -> bool {
@@ -2983,7 +2983,7 @@ where
         params: RenderParams,
         cx: &mut WindowContext<'a, 'b>,
     ) -> Box<dyn RenderedView> {
-        View::render(self, &mut RenderContext::new(params, cx))
+        View::render(self, &mut ViewContext::new(params, cx))
     }
 
     fn focus_in<'a, 'b>(
@@ -3359,7 +3359,7 @@ impl<'a, 'b, T: View> DerefMut for ViewContext<'a, 'b, T> {
     }
 }
 
-impl<'a, 'b, T: View> ViewContext<'a, 'b, T> {
+impl<'a, 'b, V: View> ViewContext<'a, 'b, V> {
     fn new(window_context: &'b mut WindowContext<'a, 'b>, view_id: usize) -> Self {
         Self {
             window_context,
@@ -3368,7 +3368,7 @@ impl<'a, 'b, T: View> ViewContext<'a, 'b, T> {
         }
     }
 
-    pub fn handle(&self) -> ViewHandle<T> {
+    pub fn handle(&self) -> ViewHandle<V> {
         ViewHandle::new(
             self.window_id,
             self.view_id,
@@ -3376,7 +3376,7 @@ impl<'a, 'b, T: View> ViewContext<'a, 'b, T> {
         )
     }
 
-    pub fn weak_handle(&self) -> WeakViewHandle<T> {
+    pub fn weak_handle(&self) -> WeakViewHandle<V> {
         WeakViewHandle::new(self.window_id, self.view_id)
     }
 
@@ -3449,7 +3449,7 @@ impl<'a, 'b, T: View> ViewContext<'a, 'b, T> {
 
     pub fn on_window_should_close<F>(&mut self, mut callback: F)
     where
-        F: 'static + FnMut(&mut T, &mut ViewContext<T>) -> bool,
+        F: 'static + FnMut(&mut V, &mut ViewContext<V>) -> bool,
     {
         let window_id = self.window_id();
         let view = self.weak_handle();
@@ -3511,10 +3511,10 @@ impl<'a, 'b, T: View> ViewContext<'a, 'b, T> {
         );
     }
 
-    pub fn replace_root_view<V, F>(&mut self, build_root_view: F) -> ViewHandle<V>
+    pub fn replace_root_view<W, F>(&mut self, build_root_view: F) -> ViewHandle<W>
     where
-        V: View,
-        F: FnOnce(&mut ViewContext<V>) -> V,
+        W: View,
+        F: FnOnce(&mut ViewContext<W>) -> W,
     {
         let window_id = self.window_id;
         self.update(|this| {
@@ -3533,7 +3533,7 @@ impl<'a, 'b, T: View> ViewContext<'a, 'b, T> {
         E: Entity,
         E::Event: 'static,
         H: Handle<E>,
-        F: 'static + FnMut(&mut T, H, &E::Event, &mut ViewContext<T>),
+        F: 'static + FnMut(&mut V, H, &E::Event, &mut ViewContext<V>),
     {
         let subscriber = self.weak_handle();
         self.window_context
@@ -3553,7 +3553,7 @@ impl<'a, 'b, T: View> ViewContext<'a, 'b, T> {
     where
         E: Entity,
         H: Handle<E>,
-        F: 'static + FnMut(&mut T, H, &mut ViewContext<T>),
+        F: 'static + FnMut(&mut V, H, &mut ViewContext<V>),
     {
         let observer = self.weak_handle();
         self.window_context
@@ -3572,7 +3572,7 @@ impl<'a, 'b, T: View> ViewContext<'a, 'b, T> {
     pub fn observe_global<G, F>(&mut self, mut callback: F) -> Subscription
     where
         G: Any,
-        F: 'static + FnMut(&mut T, &mut ViewContext<T>),
+        F: 'static + FnMut(&mut V, &mut ViewContext<V>),
     {
         let observer = self.weak_handle();
         self.window_context.observe_global::<G, _>(move |cx| {
@@ -3582,10 +3582,10 @@ impl<'a, 'b, T: View> ViewContext<'a, 'b, T> {
         })
     }
 
-    pub fn observe_focus<F, V>(&mut self, handle: &ViewHandle<V>, mut callback: F) -> Subscription
+    pub fn observe_focus<F, W>(&mut self, handle: &ViewHandle<W>, mut callback: F) -> Subscription
     where
-        F: 'static + FnMut(&mut T, ViewHandle<V>, bool, &mut ViewContext<T>),
-        V: View,
+        F: 'static + FnMut(&mut V, ViewHandle<W>, bool, &mut ViewContext<V>),
+        W: View,
     {
         let observer = self.weak_handle();
         self.window_context
@@ -3605,7 +3605,7 @@ impl<'a, 'b, T: View> ViewContext<'a, 'b, T> {
     where
         E: Entity,
         H: Handle<E>,
-        F: 'static + FnMut(&mut T, &E, &mut ViewContext<T>),
+        F: 'static + FnMut(&mut V, &E, &mut ViewContext<V>),
     {
         let observer = self.weak_handle();
         self.window_context
@@ -3620,7 +3620,7 @@ impl<'a, 'b, T: View> ViewContext<'a, 'b, T> {
 
     pub fn observe_actions<F>(&mut self, mut callback: F) -> Subscription
     where
-        F: 'static + FnMut(&mut T, TypeId, &mut ViewContext<T>),
+        F: 'static + FnMut(&mut V, TypeId, &mut ViewContext<V>),
     {
         let observer = self.weak_handle();
         self.window_context.observe_actions(move |action_id, cx| {
@@ -3634,7 +3634,7 @@ impl<'a, 'b, T: View> ViewContext<'a, 'b, T> {
 
     pub fn observe_window_activation<F>(&mut self, mut callback: F) -> Subscription
     where
-        F: 'static + FnMut(&mut T, bool, &mut ViewContext<T>),
+        F: 'static + FnMut(&mut V, bool, &mut ViewContext<V>),
     {
         let observer = self.weak_handle();
         self.window_context
@@ -3652,7 +3652,7 @@ impl<'a, 'b, T: View> ViewContext<'a, 'b, T> {
 
     pub fn observe_fullscreen<F>(&mut self, mut callback: F) -> Subscription
     where
-        F: 'static + FnMut(&mut T, bool, &mut ViewContext<T>),
+        F: 'static + FnMut(&mut V, bool, &mut ViewContext<V>),
     {
         let observer = self.weak_handle();
         self.window_context
@@ -3672,11 +3672,11 @@ impl<'a, 'b, T: View> ViewContext<'a, 'b, T> {
     where
         F: 'static
             + FnMut(
-                &mut T,
+                &mut V,
                 &Keystroke,
                 Option<&Box<dyn Action>>,
                 &MatchResult,
-                &mut ViewContext<T>,
+                &mut ViewContext<V>,
             ) -> bool,
     {
         let observer = self.weak_handle();
@@ -3697,7 +3697,7 @@ impl<'a, 'b, T: View> ViewContext<'a, 'b, T> {
 
     pub fn observe_window_bounds<F>(&mut self, mut callback: F) -> Subscription
     where
-        F: 'static + FnMut(&mut T, WindowBounds, Uuid, &mut ViewContext<T>),
+        F: 'static + FnMut(&mut V, WindowBounds, Uuid, &mut ViewContext<V>),
     {
         let observer = self.weak_handle();
         self.window_context
@@ -3715,7 +3715,7 @@ impl<'a, 'b, T: View> ViewContext<'a, 'b, T> {
 
     pub fn observe_active_labeled_tasks<F>(&mut self, mut callback: F) -> Subscription
     where
-        F: 'static + FnMut(&mut T, &mut ViewContext<T>),
+        F: 'static + FnMut(&mut V, &mut ViewContext<V>),
     {
         let observer = self.weak_handle();
         self.window_context.observe_active_labeled_tasks(move |cx| {
@@ -3730,7 +3730,7 @@ impl<'a, 'b, T: View> ViewContext<'a, 'b, T> {
         })
     }
 
-    pub fn emit(&mut self, payload: T::Event) {
+    pub fn emit(&mut self, payload: V::Event) {
         self.window_context
             .pending_effects
             .push_back(Effect::Event {
@@ -3754,7 +3754,7 @@ impl<'a, 'b, T: View> ViewContext<'a, 'b, T> {
             .dispatch_any_action_at(self.window_id, self.view_id, action)
     }
 
-    pub fn defer(&mut self, callback: impl 'static + FnOnce(&mut T, &mut ViewContext<T>)) {
+    pub fn defer(&mut self, callback: impl 'static + FnOnce(&mut V, &mut ViewContext<V>)) {
         let handle = self.handle();
         self.window_context.defer(move |cx| {
             handle.update(cx, |view, cx| {
@@ -3765,7 +3765,7 @@ impl<'a, 'b, T: View> ViewContext<'a, 'b, T> {
 
     pub fn after_window_update(
         &mut self,
-        callback: impl 'static + FnOnce(&mut T, &mut ViewContext<T>),
+        callback: impl 'static + FnOnce(&mut V, &mut ViewContext<V>),
     ) {
         let handle = self.handle();
         self.window_context.after_window_update(move |cx| {
@@ -3781,7 +3781,7 @@ impl<'a, 'b, T: View> ViewContext<'a, 'b, T> {
 
     pub fn spawn_labeled<F, Fut, S>(&mut self, task_label: &'static str, f: F) -> Task<S>
     where
-        F: FnOnce(ViewHandle<T>, AsyncAppContext) -> Fut,
+        F: FnOnce(ViewHandle<V>, AsyncAppContext) -> Fut,
         Fut: 'static + Future<Output = S>,
         S: 'static,
     {
@@ -3792,7 +3792,7 @@ impl<'a, 'b, T: View> ViewContext<'a, 'b, T> {
 
     pub fn spawn<F, Fut, S>(&mut self, f: F) -> Task<S>
     where
-        F: FnOnce(ViewHandle<T>, AsyncAppContext) -> Fut,
+        F: FnOnce(ViewHandle<V>, AsyncAppContext) -> Fut,
         Fut: 'static + Future<Output = S>,
         S: 'static,
     {
@@ -3802,79 +3802,13 @@ impl<'a, 'b, T: View> ViewContext<'a, 'b, T> {
 
     pub fn spawn_weak<F, Fut, S>(&mut self, f: F) -> Task<S>
     where
-        F: FnOnce(WeakViewHandle<T>, AsyncAppContext) -> Fut,
+        F: FnOnce(WeakViewHandle<V>, AsyncAppContext) -> Fut,
         Fut: 'static + Future<Output = S>,
         S: 'static,
     {
         let handle = self.weak_handle();
         self.window_context.spawn(|cx| f(handle, cx))
     }
-}
-
-pub struct RenderParams {
-    pub window_id: usize,
-    pub view_id: usize,
-    pub titlebar_height: f32,
-    pub hovered_region_ids: HashSet<MouseRegionId>,
-    pub clicked_region_ids: Option<(HashSet<MouseRegionId>, MouseButton)>,
-    pub refreshing: bool,
-    pub appearance: Appearance,
-}
-
-#[derive(Debug, Clone, Default)]
-pub struct MouseState {
-    pub(crate) hovered: bool,
-    pub(crate) clicked: Option<MouseButton>,
-    pub(crate) accessed_hovered: bool,
-    pub(crate) accessed_clicked: bool,
-}
-
-impl MouseState {
-    pub fn hovered(&mut self) -> bool {
-        self.accessed_hovered = true;
-        self.hovered
-    }
-
-    pub fn clicked(&mut self) -> Option<MouseButton> {
-        self.accessed_clicked = true;
-        self.clicked
-    }
-
-    pub fn accessed_hovered(&self) -> bool {
-        self.accessed_hovered
-    }
-
-    pub fn accessed_clicked(&self) -> bool {
-        self.accessed_clicked
-    }
-}
-
-impl<'a, V: View> RenderContext<'a, V> {
-    fn new(params: RenderParams, app: &'a mut AppContext) -> Self {
-        Self {
-            app,
-            window_id: params.window_id,
-            view_id: params.view_id,
-            view_type: PhantomData,
-            titlebar_height: params.titlebar_height,
-            hovered_region_ids: params.hovered_region_ids.clone(),
-            clicked_region_ids: params.clicked_region_ids.clone(),
-            refreshing: params.refreshing,
-            appearance: params.appearance,
-        }
-    }
-
-    pub fn handle(&self) -> WeakViewHandle<V> {
-        WeakViewHandle::new(self.window_id, self.view_id)
-    }
-
-    pub fn window_id(&self) -> usize {
-        self.window_id
-    }
-
-    pub fn view_id(&self) -> usize {
-        self.view_id
-    }
 
     pub fn mouse_state<Tag: 'static>(&self, region_id: usize) -> MouseState {
         let region_id = MouseRegionId::new::<Tag>(self.view_id, region_id);
@@ -3916,62 +3850,6 @@ impl<'a, V: View> RenderContext<'a, V> {
     }
 }
 
-impl<V: View> Deref for RenderContext<'_, V> {
-    type Target = AppContext;
-
-    fn deref(&self) -> &Self::Target {
-        self.app
-    }
-}
-
-impl<V: View> DerefMut for RenderContext<'_, V> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        self.app
-    }
-}
-
-impl<V: View> ReadModel for RenderContext<'_, V> {
-    fn read_model<T: Entity>(&self, handle: &ModelHandle<T>) -> &T {
-        self.app.read_model(handle)
-    }
-}
-
-impl<V: View> UpdateModel for RenderContext<'_, V> {
-    fn update_model<T: Entity, O>(
-        &mut self,
-        handle: &ModelHandle<T>,
-        update: &mut dyn FnMut(&mut T, &mut ModelContext<T>) -> O,
-    ) -> O {
-        self.app.update_model(handle, update)
-    }
-}
-
-impl<V: View> ReadView for RenderContext<'_, V> {
-    fn read_view<T: View>(&self, handle: &ViewHandle<T>) -> &T {
-        self.app.read_view(handle)
-    }
-}
-
-impl<'a, 'b, M> Deref for ViewContext<'a, 'b, M> {
-    type Target = WindowContext<'a, 'b>;
-
-    fn deref(&self) -> &Self::Target {
-        &self.window_context
-    }
-}
-
-impl<M> DerefMut for ViewContext<'_, '_, M> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.window_context
-    }
-}
-
-impl<V> ReadModel for ViewContext<'_, '_, V> {
-    fn read_model<T: Entity>(&self, handle: &ModelHandle<T>) -> &T {
-        self.window_context.read_model(handle)
-    }
-}
-
 impl<V> UpgradeModelHandle for ViewContext<'_, '_, V> {
     fn upgrade_model_handle<T: Entity>(
         &self,
@@ -3999,16 +3877,6 @@ impl<V> UpgradeViewHandle for ViewContext<'_, '_, V> {
     }
 }
 
-impl<V: View> UpgradeViewHandle for RenderContext<'_, V> {
-    fn upgrade_view_handle<T: View>(&self, handle: &WeakViewHandle<T>) -> Option<ViewHandle<T>> {
-        self.app.upgrade_view_handle(handle)
-    }
-
-    fn upgrade_any_view_handle(&self, handle: &AnyWeakViewHandle) -> Option<AnyViewHandle> {
-        self.app.upgrade_any_view_handle(handle)
-    }
-}
-
 impl<V: View> UpdateModel for ViewContext<'_, '_, V> {
     fn update_model<T: Entity, O>(
         &mut self,
@@ -4038,6 +3906,44 @@ impl<V: View> UpdateView for ViewContext<'_, '_, V> {
     }
 }
 
+pub struct RenderParams {
+    pub window_id: usize,
+    pub view_id: usize,
+    pub titlebar_height: f32,
+    pub hovered_region_ids: HashSet<MouseRegionId>,
+    pub clicked_region_ids: Option<(HashSet<MouseRegionId>, MouseButton)>,
+    pub refreshing: bool,
+    pub appearance: Appearance,
+}
+
+#[derive(Debug, Clone, Default)]
+pub struct MouseState {
+    pub(crate) hovered: bool,
+    pub(crate) clicked: Option<MouseButton>,
+    pub(crate) accessed_hovered: bool,
+    pub(crate) accessed_clicked: bool,
+}
+
+impl MouseState {
+    pub fn hovered(&mut self) -> bool {
+        self.accessed_hovered = true;
+        self.hovered
+    }
+
+    pub fn clicked(&mut self) -> Option<MouseButton> {
+        self.accessed_clicked = true;
+        self.clicked
+    }
+
+    pub fn accessed_hovered(&self) -> bool {
+        self.accessed_hovered
+    }
+
+    pub fn accessed_clicked(&self) -> bool {
+        self.accessed_clicked
+    }
+}
+
 pub trait Handle<T> {
     type Weak: 'static;
     fn id(&self) -> usize;
@@ -5078,7 +4984,7 @@ mod tests {
         }
 
         impl super::View for View {
-            fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox<Self> {
+            fn render(&mut self, _: &mut ViewContext<Self>) -> ElementBox<Self> {
                 post_inc(&mut self.render_count);
                 Empty::new().boxed()
             }
@@ -5131,7 +5037,7 @@ mod tests {
         }
 
         impl super::View for View {
-            fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox<Self> {
+            fn render(&mut self, _: &mut ViewContext<Self>) -> ElementBox<Self> {
                 Empty::new().boxed()
             }
 
@@ -5195,7 +5101,7 @@ mod tests {
         }
 
         impl super::View for View {
-            fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox<Self> {
+            fn render(&mut self, cx: &mut ViewContext<Self>) -> ElementBox<Self> {
                 enum Handler {}
                 let mouse_down_count = self.mouse_down_count.clone();
                 MouseEventHandler::<Handler>::new(0, cx, |_, _| Empty::new().boxed())
@@ -5261,7 +5167,7 @@ mod tests {
                 "View"
             }
 
-            fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox<Self> {
+            fn render(&mut self, _: &mut ViewContext<Self>) -> ElementBox<Self> {
                 Empty::new().boxed()
             }
         }
@@ -5779,7 +5685,7 @@ mod tests {
         }
 
         impl super::View for View {
-            fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox<Self> {
+            fn render(&mut self, _: &mut ViewContext<Self>) -> ElementBox<Self> {
                 Empty::new().boxed()
             }
 
@@ -5844,7 +5750,7 @@ mod tests {
         }
 
         impl super::View for View {
-            fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox<Self> {
+            fn render(&mut self, _: &mut ViewContext<Self>) -> ElementBox<Self> {
                 Empty::new().boxed()
             }
 
@@ -6020,7 +5926,7 @@ mod tests {
         }
 
         impl View for ViewA {
-            fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox<Self> {
+            fn render(&mut self, _: &mut ViewContext<Self>) -> ElementBox<Self> {
                 Empty::new().boxed()
             }
 
@@ -6038,7 +5944,7 @@ mod tests {
         }
 
         impl View for ViewB {
-            fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox<Self> {
+            fn render(&mut self, _: &mut ViewContext<Self>) -> ElementBox<Self> {
                 Empty::new().boxed()
             }
 
@@ -6190,7 +6096,7 @@ mod tests {
         }
 
         impl super::View for View {
-            fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox<Self> {
+            fn render(&mut self, _: &mut ViewContext<Self>) -> ElementBox<Self> {
                 Empty::new().boxed()
             }
 
@@ -6317,7 +6223,7 @@ mod tests {
         }
 
         impl super::View for View1 {
-            fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox<Self> {
+            fn render(&mut self, _: &mut ViewContext<Self>) -> ElementBox<Self> {
                 Empty::new().boxed()
             }
             fn ui_name() -> &'static str {
@@ -6325,7 +6231,7 @@ mod tests {
             }
         }
         impl super::View for View2 {
-            fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox<Self> {
+            fn render(&mut self, _: &mut ViewContext<Self>) -> ElementBox<Self> {
                 Empty::new().boxed()
             }
             fn ui_name() -> &'static str {
@@ -6500,7 +6406,7 @@ mod tests {
                 "test view"
             }
 
-            fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox<Self> {
+            fn render(&mut self, _: &mut ViewContext<Self>) -> ElementBox<Self> {
                 Empty::new().boxed()
             }
         }
@@ -6562,7 +6468,7 @@ mod tests {
                 "test view"
             }
 
-            fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox<Self> {
+            fn render(&mut self, _: &mut ViewContext<Self>) -> ElementBox<Self> {
                 Empty::new().named(format!("render count: {}", post_inc(&mut self.0)))
             }
         }
@@ -6651,7 +6557,7 @@ mod tests {
                 "test view"
             }
 
-            fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox<Self> {
+            fn render(&mut self, _: &mut ViewContext<Self>) -> ElementBox<Self> {
                 Empty::new().boxed()
             }
         }
@@ -6731,7 +6637,7 @@ mod tests {
                 "child view"
             }
 
-            fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox<Self> {
+            fn render(&mut self, _: &mut ViewContext<Self>) -> ElementBox<Self> {
                 self.rendered.set(true);
                 Empty::new().boxed()
             }
@@ -6756,7 +6662,7 @@ mod tests {
                 "parent view"
             }
 
-            fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox<Self> {
+            fn render(&mut self, cx: &mut ViewContext<Self>) -> ElementBox<Self> {
                 if let Some(child) = self.child.as_ref() {
                     ChildView::new(child, cx).boxed()
                 } else {
@@ -6798,7 +6704,7 @@ mod tests {
             "TestView"
         }
 
-        fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox<Self> {
+        fn render(&mut self, _: &mut ViewContext<Self>) -> ElementBox<Self> {
             Empty::new().boxed()
         }
     }

crates/gpui/src/app/test_app_context.rs 🔗

@@ -74,7 +74,7 @@ impl TestAppContext {
 
     pub fn dispatch_action<A: Action>(&self, window_id: usize, action: A) {
         let mut cx = self.cx.borrow_mut();
-        if let Some(view_id) = cx.focused_view_id(window_id) {
+        if let Some(view_id) = cx.focused_view_id {
             cx.handle_dispatch_action_from_effect(window_id, Some(view_id), &action);
         }
     }
@@ -169,11 +169,11 @@ impl TestAppContext {
 
     pub fn render<F, V, T>(&mut self, handle: &ViewHandle<V>, f: F) -> T
     where
-        F: FnOnce(&mut V, &mut RenderContext<V>) -> T,
+        F: FnOnce(&mut V, &mut ViewContext<V>) -> T,
         V: View,
     {
         handle.update(&mut *self.cx.borrow_mut(), |view, cx| {
-            let mut render_cx = RenderContext {
+            let mut render_cx = ViewContext {
                 app: cx,
                 window_id: handle.window_id(),
                 view_id: handle.id(),

crates/gpui/src/app/window.rs 🔗

@@ -87,6 +87,7 @@ pub struct WindowContext<'a: 'b, 'b> {
     app_context: &'a mut AppContext,
     pub(crate) window: &'b mut Window, // TODO: make this private?
     pub(crate) window_id: usize,
+    pub refreshing: bool,
 }
 
 impl Deref for WindowContext<'_, '_> {

crates/gpui/src/elements.rs 🔗

@@ -27,12 +27,11 @@ pub use self::{
 };
 use self::{clipped::Clipped, expanded::Expanded};
 use crate::{
-    app::window::MeasurementContext,
     geometry::{
         rect::RectF,
         vector::{vec2f, Vector2F},
     },
-    json, Action, RenderContext, SceneBuilder, SizeConstraint, View, ViewContext,
+    json, Action, SceneBuilder, SizeConstraint, View, ViewContext,
 };
 use core::panic;
 use json::ToJson;
@@ -62,7 +61,7 @@ trait AnyElement<V: View> {
         cx: &ViewContext<V>,
     ) -> Option<RectF>;
 
-    fn debug(&self, view: &V, cx: &mut ViewContext<V>) -> serde_json::Value;
+    fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value;
 
     fn size(&self) -> Vector2F;
 
@@ -82,6 +81,7 @@ pub trait Element<V: View> {
 
     fn paint(
         &mut self,
+        scene: &mut SceneBuilder,
         bounds: RectF,
         visible_bounds: RectF,
         layout: &mut Self::LayoutState,
@@ -133,81 +133,81 @@ pub trait Element<V: View> {
         }
     }
 
-    fn constrained(self) -> ConstrainedBox
+    fn constrained(self) -> ConstrainedBox<V>
     where
         Self: 'static + Sized,
     {
         ConstrainedBox::new(self.boxed())
     }
 
-    fn aligned(self) -> Align
+    fn aligned(self) -> Align<V>
     where
         Self: 'static + Sized,
     {
         Align::new(self.boxed())
     }
 
-    fn clipped(self) -> Clipped
+    fn clipped(self) -> Clipped<V>
     where
         Self: 'static + Sized,
     {
         Clipped::new(self.boxed())
     }
 
-    fn contained(self) -> Container
+    fn contained(self) -> Container<V>
     where
         Self: 'static + Sized,
     {
         Container::new(self.boxed())
     }
 
-    fn expanded(self) -> Expanded
+    fn expanded(self) -> Expanded<V>
     where
         Self: 'static + Sized,
     {
         Expanded::new(self.boxed())
     }
 
-    fn flex(self, flex: f32, expanded: bool) -> FlexItem
+    fn flex(self, flex: f32, expanded: bool) -> FlexItem<V>
     where
         Self: 'static + Sized,
     {
         FlexItem::new(self.boxed()).flex(flex, expanded)
     }
 
-    fn flex_float(self) -> FlexItem
+    fn flex_float(self) -> FlexItem<V>
     where
         Self: 'static + Sized,
     {
         FlexItem::new(self.boxed()).float()
     }
 
-    fn with_tooltip<Tag: 'static, T: View>(
+    fn with_tooltip<Tag: 'static>(
         self,
         id: usize,
         text: String,
         action: Option<Box<dyn Action>>,
         style: TooltipStyle,
-        cx: &mut RenderContext<T>,
-    ) -> Tooltip
+        cx: &mut ViewContext<V>,
+    ) -> Tooltip<V>
     where
         Self: 'static + Sized,
     {
-        Tooltip::new::<Tag, T>(id, text, action, style, self.boxed(), cx)
+        Tooltip::new::<Tag>(id, text, action, style, self.boxed(), cx)
     }
 
-    fn with_resize_handle<Tag: 'static, T: View>(
+    fn with_resize_handle<Tag: 'static>(
         self,
         element_id: usize,
         side: Side,
         handle_size: f32,
         initial_size: f32,
-        cx: &mut RenderContext<T>,
-    ) -> Resizable
+        cx: &mut ViewContext<V>,
+    ) -> Resizable<V>
     where
         Self: 'static + Sized,
     {
-        Resizable::new::<Tag, T>(
+        Resizable::new::<Tag>(
             self.boxed(),
             element_id,
             side,
@@ -270,6 +270,7 @@ impl<V: View, E: Element<V>> AnyElement<V> for Lifecycle<V, E> {
 
     fn paint(
         &mut self,
+        scene: &mut SceneBuilder,
         view: &mut V,
         origin: Vector2F,
         visible_bounds: RectF,
@@ -283,7 +284,7 @@ impl<V: View, E: Element<V>> AnyElement<V> for Lifecycle<V, E> {
                 mut layout,
             } => {
                 let bounds = RectF::new(origin, size);
-                let paint = element.paint(view, bounds, visible_bounds, &mut layout, cx);
+                let paint = element.paint(scene, bounds, visible_bounds, &mut layout, view, cx);
                 Lifecycle::PostPaint {
                     element,
                     constraint,
@@ -301,7 +302,7 @@ impl<V: View, E: Element<V>> AnyElement<V> for Lifecycle<V, E> {
                 ..
             } => {
                 let bounds = RectF::new(origin, bounds.size());
-                let paint = element.paint(view, bounds, visible_bounds, &mut layout, cx);
+                let paint = element.paint(scene, bounds, visible_bounds, &mut layout, view, cx);
                 Lifecycle::PostPaint {
                     element,
                     constraint,
@@ -334,12 +335,12 @@ impl<V: View, E: Element<V>> AnyElement<V> for Lifecycle<V, E> {
         } = self
         {
             element.rect_for_text_range(
-                view,
                 range_utf16,
                 *bounds,
                 *visible_bounds,
                 layout,
                 paint,
+                view,
                 cx,
             )
         } else {
@@ -374,7 +375,7 @@ impl<V: View, E: Element<V>> AnyElement<V> for Lifecycle<V, E> {
                 layout,
                 paint,
             } => {
-                let mut value = element.debug(view, *bounds, layout, paint, cx);
+                let mut value = element.debug(*bounds, layout, paint, view, cx);
                 if let json::Value::Object(map) = &mut value {
                     let mut new_map: crate::json::Map<String, serde_json::Value> =
                         Default::default();
@@ -403,7 +404,7 @@ impl<V: View, E: Element<V>> Default for Lifecycle<V, E> {
 }
 
 pub struct ElementBox<V: View> {
-    element: RefCell<dyn AnyElement<V>>,
+    element: Box<RefCell<dyn AnyElement<V>>>,
     view_type: PhantomData<V>,
     name: Option<Cow<'static, str>>,
 }
@@ -420,8 +421,8 @@ impl<V: View> ElementBox<V> {
 
     pub fn layout(
         &self,
-        view: &mut V,
         constraint: SizeConstraint,
+        view: &mut V,
         cx: &mut ViewContext<V>,
     ) -> Vector2F {
         self.element.borrow_mut().layout(view, constraint, cx)
@@ -429,10 +430,10 @@ impl<V: View> ElementBox<V> {
 
     pub fn paint(
         &self,
-        view: &mut V,
         scene: &mut SceneBuilder,
         origin: Vector2F,
         visible_bounds: RectF,
+        view: &mut V,
         cx: &mut ViewContext<V>,
     ) {
         self.element
@@ -442,8 +443,8 @@ impl<V: View> ElementBox<V> {
 
     pub fn rect_for_text_range(
         &self,
-        view: &V,
         range_utf16: Range<usize>,
+        view: &V,
         cx: &ViewContext<V>,
     ) -> Option<RectF> {
         self.element

crates/gpui/src/elements/canvas.rs 🔗

@@ -1,3 +1,5 @@
+use std::marker::PhantomData;
+
 use super::Element;
 use crate::{
     json::{self, json},
@@ -9,18 +11,19 @@ use pathfinder_geometry::{
     vector::{vec2f, Vector2F},
 };
 
-pub struct Canvas<V, F>(F);
+pub struct Canvas<V, F>(F, PhantomData<V>);
 
-impl<V: View, F> Canvas<V, F>
+impl<V, F> Canvas<V, F>
 where
+    V: View,
     F: FnMut(&mut SceneBuilder, RectF, RectF, &mut V, &mut ViewContext<V>),
 {
     pub fn new(f: F) -> Self {
-        Self(f)
+        Self(f, PhantomData)
     }
 }
 
-impl<V, F> Element<V> for Canvas<V, F>
+impl<V: View, F> Element<V> for Canvas<V, F>
 where
     F: FnMut(RectF, RectF, &mut ViewContext<V>),
 {
@@ -30,7 +33,8 @@ where
     fn layout(
         &mut self,
         constraint: crate::SizeConstraint,
-        _: &mut crate::LayoutContext,
+        _: &mut V,
+        _: &mut crate::ViewContext<V>,
     ) -> (Vector2F, Self::LayoutState) {
         let x = if constraint.max.x().is_finite() {
             constraint.max.x()

crates/gpui/src/elements/container.rs 🔗

@@ -308,7 +308,7 @@ impl<V: View> Element<V> for Container<V> {
         _: &Self::LayoutState,
         _: &Self::PaintState,
         view: &V,
-        cx: &crate::DebugContext,
+        cx: &ViewContext<V>,
     ) -> serde_json::Value {
         json!({
             "type": "Container",

crates/gpui/src/elements/flex.rs 🔗

@@ -101,7 +101,7 @@ impl<V: View> Flex<V> {
                             vec2f(constraint.max.x(), child_max),
                         ),
                     };
-                    let child_size = child.layout(child_constraint, cx);
+                    let child_size = child.layout(child_constraint, view, cx);
                     *remaining_space -= child_size.along(self.axis);
                     *remaining_flex -= flex;
                     *cross_axis_max = cross_axis_max.max(child_size.along(cross_axis));
@@ -434,7 +434,7 @@ impl<V: View> Element<V> for FlexItem<V> {
         view: &V,
         cx: &mut ViewContext<V>,
     ) -> (Vector2F, Self::LayoutState) {
-        let size = self.child.layout(constraint, cx);
+        let size = self.child.layout(constraint, view, cx);
         (size, ())
     }
 
@@ -445,7 +445,7 @@ impl<V: View> Element<V> for FlexItem<V> {
         visible_bounds: RectF,
         _: &mut Self::LayoutState,
         view: &V,
-        cx: &mut PaintContext,
+        cx: &mut ViewContext<V>,
     ) -> Self::PaintState {
         self.child
             .paint(scene, bounds.origin(), visible_bounds, view, cx)

crates/gpui/src/elements/list.rs 🔗

@@ -6,7 +6,7 @@ use crate::{
     json::json,
     Element, ElementBox, MouseRegion, SceneBuilder, SizeConstraint, View, ViewContext,
 };
-use std::{cell::RefCell, collections::VecDeque, ops::Range, rc::Rc};
+use std::{cell::RefCell, collections::VecDeque, fmt::Debug, ops::Range, rc::Rc};
 use sum_tree::{Bias, SumTree};
 
 pub struct List<V: View> {
@@ -24,7 +24,7 @@ pub enum Orientation {
 
 struct StateInner<V: View> {
     last_layout_width: Option<f32>,
-    render_item: Box<dyn FnMut(usize, &V, &mut ViewContext<V>) -> Option<ElementBox<V>>>,
+    render_item: Box<dyn FnMut(&mut V, usize, &mut ViewContext<V>) -> ElementBox<V>>,
     rendered_range: Range<usize>,
     items: SumTree<ListItem<V>>,
     logical_scroll_top: Option<ListOffset>,
@@ -40,14 +40,23 @@ pub struct ListOffset {
     pub offset_in_item: f32,
 }
 
-#[derive(Clone)]
 enum ListItem<V: View> {
     Unrendered,
     Rendered(Rc<ElementBox<V>>),
     Removed(f32),
 }
 
-impl<V: View> std::fmt::Debug for ListItem<V> {
+impl<V: View> Clone for ListItem<V> {
+    fn clone(&self) -> Self {
+        match self {
+            Self::Unrendered => Self::Unrendered,
+            Self::Rendered(element) => Self::Rendered(element.clone()),
+            Self::Removed(height) => Self::Removed(*height),
+        }
+    }
+}
+
+impl<V: View> Debug for ListItem<V> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         match self {
             Self::Unrendered => write!(f, "Unrendered"),
@@ -245,14 +254,13 @@ impl<V: View> Element<V> for List<V> {
         bounds: RectF,
         visible_bounds: RectF,
         scroll_top: &mut ListOffset,
-        view: &V,
+        view: &mut V,
         cx: &mut ViewContext<V>,
     ) {
         let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();
-        cx.scene.push_layer(Some(visible_bounds));
-
-        cx.scene.push_mouse_region(
-            MouseRegion::new::<Self>(cx.current_view_id(), 0, bounds).on_scroll({
+        scene.push_layer(Some(visible_bounds));
+        scene.push_mouse_region(
+            MouseRegion::new::<Self>(cx.view_id(), 0, bounds).on_scroll({
                 let state = self.state.clone();
                 let height = bounds.height();
                 let scroll_top = scroll_top.clone();
@@ -273,7 +281,7 @@ impl<V: View> Element<V> for List<V> {
             element.paint(scene, origin, visible_bounds, view, cx);
         }
 
-        cx.scene.pop_layer();
+        scene.pop_layer();
     }
 
     fn rect_for_text_range(
@@ -349,9 +357,10 @@ impl<V: View> ListState<V> {
         let handle = cx.weak_handle();
         Self(Rc::new(RefCell::new(StateInner {
             last_layout_width: None,
-            render_item: Box::new(move |ix, cx| {
-                let handle = handle.upgrade(cx)?;
-                Some(cx.render(&handle, |view, cx| render_item(view, ix, cx)))
+            render_item: Box::new(move |ix, view, cx| {
+                render_item(view, ix, cx)
+                // let handle = handle.upgrade(cx)?;
+                // Some(cx.render(&handle, |view, cx| render_item(view, ix, cx)))
             }),
             rendered_range: 0..0,
             items,
@@ -442,8 +451,8 @@ impl<V: View> StateInner<V> {
         if let Some(ListItem::Rendered(element)) = existing_element {
             Some(element.clone())
         } else {
-            let mut element = (self.render_item)(ix, cx)?;
-            element.layout(constraint, cx);
+            let mut element = (self.render_item)(view, ix, cx);
+            element.layout(constraint, view, cx);
             Some(element.into())
         }
     }
@@ -460,7 +469,7 @@ impl<V: View> StateInner<V> {
         &'a self,
         bounds: RectF,
         scroll_top: &ListOffset,
-    ) -> impl Iterator<Item = (Rc<ElementBox>, Vector2F)> + 'a {
+    ) -> impl Iterator<Item = (Rc<ElementBox<V>>, Vector2F)> + 'a {
         let mut item_origin = bounds.origin() - vec2f(0., scroll_top.offset_in_item);
         let mut cursor = self.items.cursor::<Count>();
         cursor.seek(&Count(scroll_top.item_ix), Bias::Right, &());
@@ -637,262 +646,264 @@ mod tests {
 
     #[crate::test(self)]
     fn test_layout(cx: &mut crate::AppContext) {
-        let (_, view) = cx.add_window(Default::default(), |_| TestView);
-        let constraint = SizeConstraint::new(vec2f(0., 0.), vec2f(100., 40.));
-
-        let elements = Rc::new(RefCell::new(vec![(0, 20.), (1, 30.), (2, 100.)]));
-
-        let state = view.update(cx, |_, cx| {
-            ListState::new(elements.borrow().len(), Orientation::Top, 1000.0, cx, {
-                let elements = elements.clone();
-                move |_, ix, _| {
-                    let (id, height) = elements.borrow()[ix];
-                    TestElement::new(id, height).boxed()
-                }
-            })
-        });
-
-        let mut list = List::new(state.clone());
-        let (size, _) = list.layout(
-            constraint,
-            &mut presenter.build_layout_context(vec2f(100., 40.), false, cx),
-        );
-        assert_eq!(size, vec2f(100., 40.));
-        assert_eq!(
-            state.0.borrow().items.summary().clone(),
-            ListItemSummary {
-                count: 3,
-                rendered_count: 3,
-                unrendered_count: 0,
-                height: 150.
-            }
-        );
-
-        state.0.borrow_mut().scroll(
-            &ListOffset {
-                item_ix: 0,
-                offset_in_item: 0.,
-            },
-            40.,
-            vec2f(0., -54.),
-            true,
-            &mut presenter.build_event_context(&mut Default::default(), cx),
-        );
-        let (_, logical_scroll_top) = list.layout(
-            constraint,
-            &mut presenter.build_layout_context(vec2f(100., 40.), false, cx),
-        );
-        assert_eq!(
-            logical_scroll_top,
-            ListOffset {
-                item_ix: 2,
-                offset_in_item: 4.
-            }
-        );
-        assert_eq!(state.0.borrow().scroll_top(&logical_scroll_top), 54.);
-
-        elements.borrow_mut().splice(1..2, vec![(3, 40.), (4, 50.)]);
-        elements.borrow_mut().push((5, 60.));
-        state.splice(1..2, 2);
-        state.splice(4..4, 1);
-        assert_eq!(
-            state.0.borrow().items.summary().clone(),
-            ListItemSummary {
-                count: 5,
-                rendered_count: 2,
-                unrendered_count: 3,
-                height: 120.
-            }
-        );
-
-        let (size, logical_scroll_top) = list.layout(
-            constraint,
-            &mut presenter.build_layout_context(vec2f(100., 40.), false, cx),
-        );
-        assert_eq!(size, vec2f(100., 40.));
-        assert_eq!(
-            state.0.borrow().items.summary().clone(),
-            ListItemSummary {
-                count: 5,
-                rendered_count: 5,
-                unrendered_count: 0,
-                height: 270.
-            }
-        );
-        assert_eq!(
-            logical_scroll_top,
-            ListOffset {
-                item_ix: 3,
-                offset_in_item: 4.
-            }
-        );
-        assert_eq!(state.0.borrow().scroll_top(&logical_scroll_top), 114.);
+        todo!()
+        // let (_, view) = cx.add_window(Default::default(), |_| TestView);
+        // let constraint = SizeConstraint::new(vec2f(0., 0.), vec2f(100., 40.));
+
+        // let elements = Rc::new(RefCell::new(vec![(0, 20.), (1, 30.), (2, 100.)]));
+
+        // let state = view.update(cx, |_, cx| {
+        //     ListState::new(elements.borrow().len(), Orientation::Top, 1000.0, cx, {
+        //         let elements = elements.clone();
+        //         move |_, ix, _| {
+        //             let (id, height) = elements.borrow()[ix];
+        //             TestElement::new(id, height).boxed()
+        //         }
+        //     })
+        // });
+
+        // let mut list = List::new(state.clone());
+        // let (size, _) = list.layout(
+        //     constraint,
+        //     &mut presenter.build_layout_context(vec2f(100., 40.), false, cx),
+        // );
+        // assert_eq!(size, vec2f(100., 40.));
+        // assert_eq!(
+        //     state.0.borrow().items.summary().clone(),
+        //     ListItemSummary {
+        //         count: 3,
+        //         rendered_count: 3,
+        //         unrendered_count: 0,
+        //         height: 150.
+        //     }
+        // );
+
+        // state.0.borrow_mut().scroll(
+        //     &ListOffset {
+        //         item_ix: 0,
+        //         offset_in_item: 0.,
+        //     },
+        //     40.,
+        //     vec2f(0., -54.),
+        //     true,
+        //     &mut presenter.build_event_context(&mut Default::default(), cx),
+        // );
+        // let (_, logical_scroll_top) = list.layout(
+        //     constraint,
+        //     &mut presenter.build_layout_context(vec2f(100., 40.), false, cx),
+        // );
+        // assert_eq!(
+        //     logical_scroll_top,
+        //     ListOffset {
+        //         item_ix: 2,
+        //         offset_in_item: 4.
+        //     }
+        // );
+        // assert_eq!(state.0.borrow().scroll_top(&logical_scroll_top), 54.);
+
+        // elements.borrow_mut().splice(1..2, vec![(3, 40.), (4, 50.)]);
+        // elements.borrow_mut().push((5, 60.));
+        // state.splice(1..2, 2);
+        // state.splice(4..4, 1);
+        // assert_eq!(
+        //     state.0.borrow().items.summary().clone(),
+        //     ListItemSummary {
+        //         count: 5,
+        //         rendered_count: 2,
+        //         unrendered_count: 3,
+        //         height: 120.
+        //     }
+        // );
+
+        // let (size, logical_scroll_top) = list.layout(
+        //     constraint,
+        //     &mut presenter.build_layout_context(vec2f(100., 40.), false, cx),
+        // );
+        // assert_eq!(size, vec2f(100., 40.));
+        // assert_eq!(
+        //     state.0.borrow().items.summary().clone(),
+        //     ListItemSummary {
+        //         count: 5,
+        //         rendered_count: 5,
+        //         unrendered_count: 0,
+        //         height: 270.
+        //     }
+        // );
+        // assert_eq!(
+        //     logical_scroll_top,
+        //     ListOffset {
+        //         item_ix: 3,
+        //         offset_in_item: 4.
+        //     }
+        // );
+        // assert_eq!(state.0.borrow().scroll_top(&logical_scroll_top), 114.);
     }
 
     #[crate::test(self, iterations = 10, seed = 0)]
     fn test_random(cx: &mut crate::AppContext, mut rng: StdRng) {
-        let operations = env::var("OPERATIONS")
-            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
-            .unwrap_or(10);
-
-        let (window_id, view) = cx.add_window(Default::default(), |_| TestView);
-        let mut next_id = 0;
-        let elements = Rc::new(RefCell::new(
-            (0..rng.gen_range(0..=20))
-                .map(|_| {
-                    let id = next_id;
-                    next_id += 1;
-                    (id, rng.gen_range(0..=200) as f32 / 2.0)
-                })
-                .collect::<Vec<_>>(),
-        ));
-        let orientation = *[Orientation::Top, Orientation::Bottom]
-            .choose(&mut rng)
-            .unwrap();
-        let overdraw = rng.gen_range(1..=100) as f32;
-
-        let state = view.update(cx, |_, cx| {
-            ListState::new(elements.borrow().len(), orientation, overdraw, cx, {
-                let elements = elements.clone();
-                move |_, ix, _| {
-                    let (id, height) = elements.borrow()[ix];
-                    TestElement::new(id, height).boxed()
-                }
-            })
-        });
-
-        let mut width = rng.gen_range(0..=2000) as f32 / 2.;
-        let mut height = rng.gen_range(0..=2000) as f32 / 2.;
-        log::info!("orientation: {:?}", orientation);
-        log::info!("overdraw: {}", overdraw);
-        log::info!("elements: {:?}", elements.borrow());
-        log::info!("size: ({:?}, {:?})", width, height);
-        log::info!("==================");
-
-        let mut last_logical_scroll_top = None;
-        for _ in 0..operations {
-            match rng.gen_range(0..=100) {
-                0..=29 if last_logical_scroll_top.is_some() => {
-                    let delta = vec2f(0., rng.gen_range(-overdraw..=overdraw));
-                    log::info!(
-                        "Scrolling by {:?}, previous scroll top: {:?}",
-                        delta,
-                        last_logical_scroll_top.unwrap()
-                    );
-                    state.0.borrow_mut().scroll(
-                        last_logical_scroll_top.as_ref().unwrap(),
-                        height,
-                        delta,
-                        true,
-                        &mut presenter.build_event_context(&mut Default::default(), cx),
-                    );
-                }
-                30..=34 => {
-                    width = rng.gen_range(0..=2000) as f32 / 2.;
-                    log::info!("changing width: {:?}", width);
-                }
-                35..=54 => {
-                    height = rng.gen_range(0..=1000) as f32 / 2.;
-                    log::info!("changing height: {:?}", height);
-                }
-                _ => {
-                    let mut elements = elements.borrow_mut();
-                    let end_ix = rng.gen_range(0..=elements.len());
-                    let start_ix = rng.gen_range(0..=end_ix);
-                    let new_elements = (0..rng.gen_range(0..10))
-                        .map(|_| {
-                            let id = next_id;
-                            next_id += 1;
-                            (id, rng.gen_range(0..=200) as f32 / 2.)
-                        })
-                        .collect::<Vec<_>>();
-                    log::info!("splice({:?}, {:?})", start_ix..end_ix, new_elements);
-                    state.splice(start_ix..end_ix, new_elements.len());
-                    elements.splice(start_ix..end_ix, new_elements);
-                    for (ix, item) in state.0.borrow().items.cursor::<()>().enumerate() {
-                        if let ListItem::Rendered(element) = item {
-                            let (expected_id, _) = elements[ix];
-                            element.with_metadata(|metadata: Option<&usize>| {
-                                assert_eq!(*metadata.unwrap(), expected_id);
-                            });
-                        }
-                    }
-                }
-            }
-
-            let mut list = List::new(state.clone());
-            let window_size = vec2f(width, height);
-            let (size, logical_scroll_top) = list.layout(
-                SizeConstraint::new(vec2f(0., 0.), window_size),
-                &mut presenter.build_layout_context(window_size, false, cx),
-            );
-            assert_eq!(size, window_size);
-            last_logical_scroll_top = Some(logical_scroll_top);
-
-            let state = state.0.borrow();
-            log::info!("items {:?}", state.items.items(&()));
-
-            let scroll_top = state.scroll_top(&logical_scroll_top);
-            let rendered_top = (scroll_top - overdraw).max(0.);
-            let rendered_bottom = scroll_top + height + overdraw;
-            let mut item_top = 0.;
-
-            log::info!(
-                "rendered top {:?}, rendered bottom {:?}, scroll top {:?}",
-                rendered_top,
-                rendered_bottom,
-                scroll_top,
-            );
-
-            let mut first_rendered_element_top = None;
-            let mut last_rendered_element_bottom = None;
-            assert_eq!(state.items.summary().count, elements.borrow().len());
-            for (ix, item) in state.items.cursor::<()>().enumerate() {
-                match item {
-                    ListItem::Unrendered => {
-                        let item_bottom = item_top;
-                        assert!(item_bottom <= rendered_top || item_top >= rendered_bottom);
-                        item_top = item_bottom;
-                    }
-                    ListItem::Removed(height) => {
-                        let (id, expected_height) = elements.borrow()[ix];
-                        assert_eq!(
-                            *height, expected_height,
-                            "element {} height didn't match",
-                            id
-                        );
-                        let item_bottom = item_top + height;
-                        assert!(item_bottom <= rendered_top || item_top >= rendered_bottom);
-                        item_top = item_bottom;
-                    }
-                    ListItem::Rendered(element) => {
-                        let (expected_id, expected_height) = elements.borrow()[ix];
-                        element.with_metadata(|metadata: Option<&usize>| {
-                            assert_eq!(*metadata.unwrap(), expected_id);
-                        });
-                        assert_eq!(element.size().y(), expected_height);
-                        let item_bottom = item_top + element.size().y();
-                        first_rendered_element_top.get_or_insert(item_top);
-                        last_rendered_element_bottom = Some(item_bottom);
-                        assert!(item_bottom > rendered_top || item_top < rendered_bottom);
-                        item_top = item_bottom;
-                    }
-                }
-            }
-
-            match orientation {
-                Orientation::Top => {
-                    if let Some(first_rendered_element_top) = first_rendered_element_top {
-                        assert!(first_rendered_element_top <= scroll_top);
-                    }
-                }
-                Orientation::Bottom => {
-                    if let Some(last_rendered_element_bottom) = last_rendered_element_bottom {
-                        assert!(last_rendered_element_bottom >= scroll_top + height);
-                    }
-                }
-            }
-        }
+        todo!()
+        // let operations = env::var("OPERATIONS")
+        //     .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
+        //     .unwrap_or(10);
+
+        // let (window_id, view) = cx.add_window(Default::default(), |_| TestView);
+        // let mut next_id = 0;
+        // let elements = Rc::new(RefCell::new(
+        //     (0..rng.gen_range(0..=20))
+        //         .map(|_| {
+        //             let id = next_id;
+        //             next_id += 1;
+        //             (id, rng.gen_range(0..=200) as f32 / 2.0)
+        //         })
+        //         .collect::<Vec<_>>(),
+        // ));
+        // let orientation = *[Orientation::Top, Orientation::Bottom]
+        //     .choose(&mut rng)
+        //     .unwrap();
+        // let overdraw = rng.gen_range(1..=100) as f32;
+
+        // let state = view.update(cx, |_, cx| {
+        //     ListState::new(elements.borrow().len(), orientation, overdraw, cx, {
+        //         let elements = elements.clone();
+        //         move |_, ix, _| {
+        //             let (id, height) = elements.borrow()[ix];
+        //             TestElement::new(id, height).boxed()
+        //         }
+        //     })
+        // });
+
+        // let mut width = rng.gen_range(0..=2000) as f32 / 2.;
+        // let mut height = rng.gen_range(0..=2000) as f32 / 2.;
+        // log::info!("orientation: {:?}", orientation);
+        // log::info!("overdraw: {}", overdraw);
+        // log::info!("elements: {:?}", elements.borrow());
+        // log::info!("size: ({:?}, {:?})", width, height);
+        // log::info!("==================");
+
+        // let mut last_logical_scroll_top = None;
+        // for _ in 0..operations {
+        //     match rng.gen_range(0..=100) {
+        //         0..=29 if last_logical_scroll_top.is_some() => {
+        //             let delta = vec2f(0., rng.gen_range(-overdraw..=overdraw));
+        //             log::info!(
+        //                 "Scrolling by {:?}, previous scroll top: {:?}",
+        //                 delta,
+        //                 last_logical_scroll_top.unwrap()
+        //             );
+        //             state.0.borrow_mut().scroll(
+        //                 last_logical_scroll_top.as_ref().unwrap(),
+        //                 height,
+        //                 delta,
+        //                 true,
+        //                 &mut presenter.build_event_context(&mut Default::default(), cx),
+        //             );
+        //         }
+        //         30..=34 => {
+        //             width = rng.gen_range(0..=2000) as f32 / 2.;
+        //             log::info!("changing width: {:?}", width);
+        //         }
+        //         35..=54 => {
+        //             height = rng.gen_range(0..=1000) as f32 / 2.;
+        //             log::info!("changing height: {:?}", height);
+        //         }
+        //         _ => {
+        //             let mut elements = elements.borrow_mut();
+        //             let end_ix = rng.gen_range(0..=elements.len());
+        //             let start_ix = rng.gen_range(0..=end_ix);
+        //             let new_elements = (0..rng.gen_range(0..10))
+        //                 .map(|_| {
+        //                     let id = next_id;
+        //                     next_id += 1;
+        //                     (id, rng.gen_range(0..=200) as f32 / 2.)
+        //                 })
+        //                 .collect::<Vec<_>>();
+        //             log::info!("splice({:?}, {:?})", start_ix..end_ix, new_elements);
+        //             state.splice(start_ix..end_ix, new_elements.len());
+        //             elements.splice(start_ix..end_ix, new_elements);
+        //             for (ix, item) in state.0.borrow().items.cursor::<()>().enumerate() {
+        //                 if let ListItem::Rendered(element) = item {
+        //                     let (expected_id, _) = elements[ix];
+        //                     element.with_metadata(|metadata: Option<&usize>| {
+        //                         assert_eq!(*metadata.unwrap(), expected_id);
+        //                     });
+        //                 }
+        //             }
+        //         }
+        //     }
+
+        //     let mut list = List::new(state.clone());
+        //     let window_size = vec2f(width, height);
+        //     let (size, logical_scroll_top) = list.layout(
+        //         SizeConstraint::new(vec2f(0., 0.), window_size),
+        //         &mut presenter.build_layout_context(window_size, false, cx),
+        //     );
+        //     assert_eq!(size, window_size);
+        //     last_logical_scroll_top = Some(logical_scroll_top);
+
+        //     let state = state.0.borrow();
+        //     log::info!("items {:?}", state.items.items(&()));
+
+        //     let scroll_top = state.scroll_top(&logical_scroll_top);
+        //     let rendered_top = (scroll_top - overdraw).max(0.);
+        //     let rendered_bottom = scroll_top + height + overdraw;
+        //     let mut item_top = 0.;
+
+        //     log::info!(
+        //         "rendered top {:?}, rendered bottom {:?}, scroll top {:?}",
+        //         rendered_top,
+        //         rendered_bottom,
+        //         scroll_top,
+        //     );
+
+        //     let mut first_rendered_element_top = None;
+        //     let mut last_rendered_element_bottom = None;
+        //     assert_eq!(state.items.summary().count, elements.borrow().len());
+        //     for (ix, item) in state.items.cursor::<()>().enumerate() {
+        //         match item {
+        //             ListItem::Unrendered => {
+        //                 let item_bottom = item_top;
+        //                 assert!(item_bottom <= rendered_top || item_top >= rendered_bottom);
+        //                 item_top = item_bottom;
+        //             }
+        //             ListItem::Removed(height) => {
+        //                 let (id, expected_height) = elements.borrow()[ix];
+        //                 assert_eq!(
+        //                     *height, expected_height,
+        //                     "element {} height didn't match",
+        //                     id
+        //                 );
+        //                 let item_bottom = item_top + height;
+        //                 assert!(item_bottom <= rendered_top || item_top >= rendered_bottom);
+        //                 item_top = item_bottom;
+        //             }
+        //             ListItem::Rendered(element) => {
+        //                 let (expected_id, expected_height) = elements.borrow()[ix];
+        //                 element.with_metadata(|metadata: Option<&usize>| {
+        //                     assert_eq!(*metadata.unwrap(), expected_id);
+        //                 });
+        //                 assert_eq!(element.size().y(), expected_height);
+        //                 let item_bottom = item_top + element.size().y();
+        //                 first_rendered_element_top.get_or_insert(item_top);
+        //                 last_rendered_element_bottom = Some(item_bottom);
+        //                 assert!(item_bottom > rendered_top || item_top < rendered_bottom);
+        //                 item_top = item_bottom;
+        //             }
+        //         }
+        //     }
+
+        //     match orientation {
+        //         Orientation::Top => {
+        //             if let Some(first_rendered_element_top) = first_rendered_element_top {
+        //                 assert!(first_rendered_element_top <= scroll_top);
+        //             }
+        //         }
+        //         Orientation::Bottom => {
+        //             if let Some(last_rendered_element_bottom) = last_rendered_element_bottom {
+        //                 assert!(last_rendered_element_bottom >= scroll_top + height);
+        //             }
+        //         }
+        //     }
+        // }
     }
 
     struct TestView;

crates/gpui/src/elements/mouse_event_handler.rs 🔗

@@ -233,14 +233,16 @@ impl<Tag, V: View> Element<V> for MouseEventHandler<Tag, V> {
         cx: &mut ViewContext<V>,
     ) -> Self::PaintState {
         if self.above {
-            self.child.paint(bounds.origin(), visible_bounds, view, cx);
+            self.child
+                .paint(scene, bounds.origin(), visible_bounds, view, cx);
 
             cx.paint_layer(None, |cx| {
-                self.paint_regions(bounds, visible_bounds, cx);
+                self.paint_regions(scene, bounds, visible_bounds, cx);
             });
         } else {
-            self.paint_regions(bounds, visible_bounds, cx);
-            self.child.paint(bounds.origin(), visible_bounds, view, cx);
+            self.paint_regions(scene, bounds, visible_bounds, cx);
+            self.child
+                .paint(scene, bounds.origin(), visible_bounds, view, cx);
         }
     }
 

crates/gpui/src/elements/overlay.rs 🔗

@@ -131,7 +131,7 @@ impl<V: View> Element<V> for Overlay<V> {
         } else {
             constraint
         };
-        let size = self.child.layout(constraint, cx);
+        let size = self.child.layout(constraint, view, cx);
         (Vector2F::zero(), size)
     }
 

crates/gpui/src/elements/resizable.rs 🔗

@@ -7,7 +7,7 @@ use crate::{
     geometry::rect::RectF,
     platform::{CursorStyle, MouseButton},
     scene::MouseDrag,
-    Axis, Element, ElementBox, ElementStateHandle, MouseRegion, RenderContext, View,
+    Axis, Element, ElementBox, ElementStateHandle, MouseRegion, SceneBuilder, View, ViewContext,
 };
 
 use super::{ConstrainedBox, Hook};
@@ -75,22 +75,22 @@ struct ResizeHandleState {
     custom_dimension: Cell<f32>,
 }
 
-pub struct Resizable {
+pub struct Resizable<V: View> {
     side: Side,
     handle_size: f32,
-    child: ElementBox,
+    child: ElementBox<V>,
     state: Rc<ResizeHandleState>,
     _state_handle: ElementStateHandle<Rc<ResizeHandleState>>,
 }
 
-impl Resizable {
+impl<V: View> Resizable<V> {
     pub fn new<Tag: 'static, T: View>(
-        child: ElementBox,
+        child: ElementBox<V>,
         element_id: usize,
         side: Side,
         handle_size: f32,
         initial_size: f32,
-        cx: &mut RenderContext<T>,
+        cx: &mut ViewContext<V>,
     ) -> Self {
         let state_handle = cx.element_state::<Tag, Rc<ResizeHandleState>>(
             element_id,
@@ -132,24 +132,27 @@ impl Resizable {
     }
 }
 
-impl Element for Resizable {
+impl<V: View> Element<V> for Resizable<V> {
     type LayoutState = ();
     type PaintState = ();
 
     fn layout(
         &mut self,
         constraint: crate::SizeConstraint,
-        cx: &mut crate::LayoutContext,
+        view: &mut V,
+        cx: &mut ViewContext<V>,
     ) -> (Vector2F, Self::LayoutState) {
-        (self.child.layout(constraint, cx), ())
+        (self.child.layout(constraint, view, cx), ())
     }
 
     fn paint(
         &mut self,
+        scene: &mut SceneBuilder,
         bounds: pathfinder_geometry::rect::RectF,
         visible_bounds: pathfinder_geometry::rect::RectF,
         _child_size: &mut Self::LayoutState,
-        cx: &mut crate::PaintContext,
+        view: &mut V,
+        cx: &mut ViewContext<V>,
     ) -> Self::PaintState {
         cx.scene.push_stacking_context(None, None);
 
@@ -186,7 +189,8 @@ impl Element for Resizable {
 
         cx.scene.pop_stacking_context();
 
-        self.child.paint(bounds.origin(), visible_bounds, cx);
+        self.child
+            .paint(scene, bounds.origin(), visible_bounds, view, cx);
     }
 
     fn rect_for_text_range(
@@ -196,9 +200,10 @@ impl Element for Resizable {
         _visible_bounds: pathfinder_geometry::rect::RectF,
         _layout: &Self::LayoutState,
         _paint: &Self::PaintState,
-        cx: &crate::MeasurementContext,
+        view: &V,
+        cx: &ViewContext<V>,
     ) -> Option<pathfinder_geometry::rect::RectF> {
-        self.child.rect_for_text_range(range_utf16, cx)
+        self.child.rect_for_text_range(range_utf16, view, cx)
     }
 
     fn debug(
@@ -206,10 +211,11 @@ impl Element for Resizable {
         _bounds: pathfinder_geometry::rect::RectF,
         _layout: &Self::LayoutState,
         _paint: &Self::PaintState,
-        cx: &crate::DebugContext,
+        view: &V,
+        cx: &ViewContext<V>,
     ) -> serde_json::Value {
         json!({
-            "child": self.child.debug(cx),
+            "child": self.child.debug(view, cx),
         })
     }
 }

crates/gpui/src/elements/stack.rs 🔗

@@ -3,41 +3,41 @@ use std::ops::Range;
 use crate::{
     geometry::{rect::RectF, vector::Vector2F},
     json::{self, json, ToJson},
-    window::MeasurementContext,
-    DebugContext, Element, ElementBox, LayoutContext, PaintContext, SizeConstraint,
+    Element, ElementBox, SceneBuilder, SizeConstraint, View, ViewContext,
 };
 
 /// Element which renders it's children in a stack on top of each other.
 /// The first child determines the size of the others.
 #[derive(Default)]
-pub struct Stack {
-    children: Vec<ElementBox>,
+pub struct Stack<V: View> {
+    children: Vec<ElementBox<V>>,
 }
 
-impl Stack {
+impl<V: View> Stack<V> {
     pub fn new() -> Self {
         Self::default()
     }
 }
 
-impl Element for Stack {
+impl<V: View> Element<V> for Stack<V> {
     type LayoutState = ();
     type PaintState = ();
 
     fn layout(
         &mut self,
         mut constraint: SizeConstraint,
-        cx: &mut LayoutContext,
+        view: &mut V,
+        cx: &mut ViewContext<V>,
     ) -> (Vector2F, Self::LayoutState) {
         let mut size = constraint.min;
         let mut children = self.children.iter_mut();
         if let Some(bottom_child) = children.next() {
-            size = bottom_child.layout(constraint, cx);
+            size = bottom_child.layout(constraint, view, cx);
             constraint = SizeConstraint::strict(size);
         }
 
         for child in children {
-            child.layout(constraint, cx);
+            child.layout(constraint, view, cx);
         }
 
         (size, ())
@@ -45,14 +45,16 @@ impl Element for Stack {
 
     fn paint(
         &mut self,
+        scene: &mut SceneBuilder,
         bounds: RectF,
         visible_bounds: RectF,
         _: &mut Self::LayoutState,
-        cx: &mut PaintContext,
+        view: &mut V,
+        cx: &mut ViewContext<V>,
     ) -> Self::PaintState {
         for child in &mut self.children {
             cx.paint_layer(None, |cx| {
-                child.paint(bounds.origin(), visible_bounds, cx);
+                child.paint(scene, bounds.origin(), visible_bounds, view, cx);
             });
         }
     }
@@ -64,12 +66,13 @@ impl Element for Stack {
         _: RectF,
         _: &Self::LayoutState,
         _: &Self::PaintState,
-        cx: &MeasurementContext,
+        view: &V,
+        cx: &ViewContext<V>,
     ) -> Option<RectF> {
         self.children
             .iter()
             .rev()
-            .find_map(|child| child.rect_for_text_range(range_utf16.clone(), cx))
+            .find_map(|child| child.rect_for_text_range(range_utf16.clone(), view, cx))
     }
 
     fn debug(
@@ -77,18 +80,19 @@ impl Element for Stack {
         bounds: RectF,
         _: &Self::LayoutState,
         _: &Self::PaintState,
-        cx: &DebugContext,
+        view: &V,
+        cx: &ViewContext<V>,
     ) -> json::Value {
         json!({
             "type": "Stack",
             "bounds": bounds.to_json(),
-            "children": self.children.iter().map(|child| child.debug(cx)).collect::<Vec<json::Value>>()
+            "children": self.children.iter().map(|child| child.debug(view, cx)).collect::<Vec<json::Value>>()
         })
     }
 }
 
-impl Extend<ElementBox> for Stack {
-    fn extend<T: IntoIterator<Item = ElementBox>>(&mut self, children: T) {
+impl<V: View> Extend<ElementBox<V>> for Stack<V> {
+    fn extend<T: IntoIterator<Item = ElementBox<V>>>(&mut self, children: T) {
         self.children.extend(children)
     }
 }

crates/gpui/src/elements/svg.rs 🔗

@@ -8,9 +8,7 @@ use crate::{
         rect::RectF,
         vector::{vec2f, Vector2F},
     },
-    scene,
-    window::MeasurementContext,
-    DebugContext, Element, LayoutContext, PaintContext, SizeConstraint,
+    scene, Element, SceneBuilder, SizeConstraint, View, ViewContext,
 };
 
 pub struct Svg {
@@ -32,14 +30,15 @@ impl Svg {
     }
 }
 
-impl Element for Svg {
+impl<V: View> Element<V> for Svg {
     type LayoutState = Option<usvg::Tree>;
     type PaintState = ();
 
     fn layout(
         &mut self,
         constraint: SizeConstraint,
-        cx: &mut LayoutContext,
+        view: &mut V,
+        cx: &mut ViewContext<V>,
     ) -> (Vector2F, Self::LayoutState) {
         match cx.asset_cache.svg(&self.path) {
             Ok(tree) => {
@@ -59,13 +58,15 @@ impl Element for Svg {
 
     fn paint(
         &mut self,
+        scene: &mut SceneBuilder,
         bounds: RectF,
         _visible_bounds: RectF,
         svg: &mut Self::LayoutState,
-        cx: &mut PaintContext,
+        _: &mut V,
+        _: &mut ViewContext<V>,
     ) {
         if let Some(svg) = svg.clone() {
-            cx.scene.push_icon(scene::Icon {
+            scene.push_icon(scene::Icon {
                 bounds,
                 svg,
                 path: self.path.clone(),
@@ -81,7 +82,8 @@ impl Element for Svg {
         _: RectF,
         _: &Self::LayoutState,
         _: &Self::PaintState,
-        _: &MeasurementContext,
+        _: &V,
+        _: &ViewContext<V>,
     ) -> Option<RectF> {
         None
     }
@@ -91,7 +93,8 @@ impl Element for Svg {
         bounds: RectF,
         _: &Self::LayoutState,
         _: &Self::PaintState,
-        _: &DebugContext,
+        _: &V,
+        _: &ViewContext<V>,
     ) -> serde_json::Value {
         json!({
             "type": "Svg",

crates/gpui/src/elements/text.rs 🔗

@@ -7,8 +7,7 @@ use crate::{
     },
     json::{ToJson, Value},
     text_layout::{Line, RunStyle, ShapedBoundary},
-    window::MeasurementContext,
-    DebugContext, Element, FontCache, LayoutContext, PaintContext, SizeConstraint, TextLayoutCache,
+    Element, FontCache, SceneBuilder, SizeConstraint, TextLayoutCache, View, ViewContext,
 };
 use log::warn;
 use serde_json::json;
@@ -53,14 +52,15 @@ impl Text {
     }
 }
 
-impl Element for Text {
+impl<V: View> Element<V> for Text {
     type LayoutState = LayoutState;
     type PaintState = ();
 
     fn layout(
         &mut self,
         constraint: SizeConstraint,
-        cx: &mut LayoutContext,
+        _: &mut V,
+        cx: &mut ViewContext<V>,
     ) -> (Vector2F, Self::LayoutState) {
         // Convert the string and highlight ranges into an iterator of highlighted chunks.
 
@@ -99,7 +99,7 @@ impl Element for Text {
             chunks,
             &self.style,
             cx.text_layout_cache,
-            cx.font_cache,
+            &cx.font_cache,
             usize::MAX,
             self.text.matches('\n').count() + 1,
         );
@@ -143,10 +143,12 @@ impl Element for Text {
 
     fn paint(
         &mut self,
+        scene: &mut SceneBuilder,
         bounds: RectF,
         visible_bounds: RectF,
         layout: &mut Self::LayoutState,
-        cx: &mut PaintContext,
+        _: &mut V,
+        cx: &mut ViewContext<V>,
     ) -> Self::PaintState {
         let mut origin = bounds.origin();
         let empty = Vec::new();
@@ -163,6 +165,7 @@ impl Element for Text {
             if boundaries.intersects(visible_bounds) {
                 if self.soft_wrap {
                     line.paint_wrapped(
+                        scene,
                         origin,
                         visible_bounds,
                         layout.line_height,
@@ -170,7 +173,7 @@ impl Element for Text {
                         cx,
                     );
                 } else {
-                    line.paint(origin, visible_bounds, layout.line_height, cx);
+                    line.paint(scene, origin, visible_bounds, layout.line_height, cx);
                 }
             }
             origin.set_y(boundaries.max_y());
@@ -184,7 +187,8 @@ impl Element for Text {
         _: RectF,
         _: &Self::LayoutState,
         _: &Self::PaintState,
-        _: &MeasurementContext,
+        _: &V,
+        _: &ViewContext<V>,
     ) -> Option<RectF> {
         None
     }
@@ -194,7 +198,8 @@ impl Element for Text {
         bounds: RectF,
         _: &Self::LayoutState,
         _: &Self::PaintState,
-        _: &DebugContext,
+        _: &V,
+        _: &ViewContext<V>,
     ) -> Value {
         json!({
             "type": "Text",
@@ -272,7 +277,7 @@ pub fn layout_highlighted_chunks<'a>(
 mod tests {
     use super::*;
     use crate::{
-        elements::Empty, fonts, platform, AppContext, ElementBox, Entity, RenderContext, View,
+        elements::Empty, fonts, platform, AppContext, ElementBox, Entity, View, ViewContext,
     };
 
     #[crate::test(self)]
@@ -305,7 +310,7 @@ mod tests {
             "TestView"
         }
 
-        fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox {
+        fn render(&mut self, _: &mut ViewContext<Self>) -> ElementBox<Self> {
             Empty::new().boxed()
         }
     }

crates/gpui/src/elements/tooltip.rs 🔗

@@ -6,9 +6,7 @@ use crate::{
     fonts::TextStyle,
     geometry::{rect::RectF, vector::Vector2F},
     json::json,
-    window::MeasurementContext,
-    Action, Axis, ElementStateHandle, LayoutContext, PaintContext, RenderContext, SizeConstraint,
-    Task, View,
+    Action, Axis, ElementStateHandle, SceneBuilder, SizeConstraint, Task, View, ViewContext,
 };
 use serde::Deserialize;
 use std::{
@@ -20,9 +18,9 @@ use std::{
 
 const DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(500);
 
-pub struct Tooltip {
-    child: ElementBox,
-    tooltip: Option<ElementBox>,
+pub struct Tooltip<V: View> {
+    child: ElementBox<V>,
+    tooltip: Option<ElementBox<V>>,
     _state: ElementStateHandle<Rc<TooltipState>>,
 }
 
@@ -50,18 +48,19 @@ pub struct KeystrokeStyle {
     text: TextStyle,
 }
 
-impl Tooltip {
+impl<V: View> Tooltip<V> {
     pub fn new<Tag: 'static, T: View>(
         id: usize,
         text: String,
         action: Option<Box<dyn Action>>,
         style: TooltipStyle,
-        child: ElementBox,
-        cx: &mut RenderContext<T>,
+        child: ElementBox<V>,
+        view: &mut V,
+        cx: &mut ViewContext<V>,
     ) -> Self {
         struct ElementState<Tag>(Tag);
         struct MouseEventHandlerState<Tag>(Tag);
-        let focused_view_id = cx.focused_view_id(cx.window_id);
+        let focused_view_id = cx.focused_view_id();
 
         let state_handle = cx.default_element_state::<ElementState<Tag>, Rc<TooltipState>>(id);
         let state = state_handle.read(cx).clone();
@@ -94,35 +93,36 @@ impl Tooltip {
         } else {
             None
         };
-        let child = MouseEventHandler::<MouseEventHandlerState<Tag>>::new(id, cx, |_, _| child)
-            .on_hover(move |e, cx| {
-                let position = e.position;
-                let window_id = cx.window_id();
-                if let Some(view_id) = cx.view_id() {
-                    if e.started {
-                        if !state.visible.get() {
-                            state.position.set(position);
+        let child =
+            MouseEventHandler::<MouseEventHandlerState<Tag>>::new(id, view, cx, |_, _| child)
+                .on_hover(move |e, cx| {
+                    let position = e.position;
+                    let window_id = cx.window_id();
+                    if let Some(view_id) = cx.view_id() {
+                        if e.started {
+                            if !state.visible.get() {
+                                state.position.set(position);
 
-                            let mut debounce = state.debounce.borrow_mut();
-                            if debounce.is_none() {
-                                *debounce = Some(cx.spawn({
-                                    let state = state.clone();
-                                    |mut cx| async move {
-                                        cx.background().timer(DEBOUNCE_TIMEOUT).await;
-                                        state.visible.set(true);
-                                        cx.update(|cx| cx.notify_view(window_id, view_id));
-                                    }
-                                }));
+                                let mut debounce = state.debounce.borrow_mut();
+                                if debounce.is_none() {
+                                    *debounce = Some(cx.spawn({
+                                        let state = state.clone();
+                                        |mut cx| async move {
+                                            cx.background().timer(DEBOUNCE_TIMEOUT).await;
+                                            state.visible.set(true);
+                                            cx.update(|cx| cx.notify_view(window_id, view_id));
+                                        }
+                                    }));
+                                }
                             }
+                        } else {
+                            state.visible.set(false);
+                            state.debounce.take();
+                            cx.notify();
                         }
-                    } else {
-                        state.visible.set(false);
-                        state.debounce.take();
-                        cx.notify();
                     }
-                }
-            })
-            .boxed();
+                })
+                .boxed();
         Self {
             child,
             tooltip,
@@ -168,32 +168,40 @@ impl Tooltip {
     }
 }
 
-impl Element for Tooltip {
+impl<V: View> Element<V> for Tooltip<V> {
     type LayoutState = ();
     type PaintState = ();
 
     fn layout(
         &mut self,
         constraint: SizeConstraint,
-        cx: &mut LayoutContext,
+        view: &mut V,
+        cx: &mut ViewContext<V>,
     ) -> (Vector2F, Self::LayoutState) {
-        let size = self.child.layout(constraint, cx);
+        let size = self.child.layout(constraint, view, cx);
         if let Some(tooltip) = self.tooltip.as_mut() {
-            tooltip.layout(SizeConstraint::new(Vector2F::zero(), cx.window_size), cx);
+            tooltip.layout(
+                SizeConstraint::new(Vector2F::zero(), cx.window_size),
+                view,
+                cx,
+            );
         }
         (size, ())
     }
 
     fn paint(
         &mut self,
+        scene: &mut SceneBuilder,
         bounds: RectF,
         visible_bounds: RectF,
         _: &mut Self::LayoutState,
-        cx: &mut PaintContext,
+        view: &mut V,
+        cx: &mut ViewContext<V>,
     ) {
-        self.child.paint(bounds.origin(), visible_bounds, cx);
+        self.child
+            .paint(scene, bounds.origin(), visible_bounds, view, cx);
         if let Some(tooltip) = self.tooltip.as_mut() {
-            tooltip.paint(bounds.origin(), visible_bounds, cx);
+            tooltip.paint(scene, bounds.origin(), visible_bounds, view, cx);
         }
     }
 
@@ -204,9 +212,10 @@ impl Element for Tooltip {
         _: RectF,
         _: &Self::LayoutState,
         _: &Self::PaintState,
-        cx: &MeasurementContext,
+        view: &V,
+        cx: &ViewContext<V>,
     ) -> Option<RectF> {
-        self.child.rect_for_text_range(range, cx)
+        self.child.rect_for_text_range(range, view, cx)
     }
 
     fn debug(
@@ -214,11 +223,12 @@ impl Element for Tooltip {
         _: RectF,
         _: &Self::LayoutState,
         _: &Self::PaintState,
-        cx: &crate::DebugContext,
+        view: &V,
+        cx: &ViewContext<V>,
     ) -> serde_json::Value {
         json!({
-            "child": self.child.debug(cx),
-            "tooltip": self.tooltip.as_ref().map(|t| t.debug(cx)),
+            "child": self.child.debug(view, cx),
+            "tooltip": self.tooltip.as_ref().map(|t| t.debug(view, cx)),
         })
     }
 }

crates/gpui/src/elements/uniform_list.rs 🔗

@@ -104,7 +104,7 @@ impl<V: View> UniformList<V> {
         mut delta: Vector2F,
         precise: bool,
         scroll_max: f32,
-        cx: &mut EventContext,
+        cx: &mut ViewContext<V>,
     ) -> bool {
         if !precise {
             delta *= 20.;

crates/gpui/src/gpui.rs 🔗

@@ -19,7 +19,7 @@ pub use scene::{Border, CursorRegion, MouseRegion, MouseRegionId, Quad, Scene, S
 pub mod text_layout;
 pub use text_layout::TextLayoutCache;
 mod util;
-pub use elements::{Element, ElementBox, ElementRc};
+pub use elements::{Element, ElementBox};
 pub mod executor;
 pub use executor::Task;
 pub mod color;
@@ -27,10 +27,7 @@ pub mod json;
 pub mod keymap_matcher;
 pub mod platform;
 pub use gpui_macros::test;
-pub use window::{
-    Axis, DebugContext, EventContext, LayoutContext, MeasurementContext, PaintContext,
-    SizeConstraint, Vector2FExt,
-};
+pub use window::{Axis, SizeConstraint, Vector2FExt};
 
 pub use anyhow;
 pub use serde_json;

crates/gpui/src/scene/mouse_region.rs 🔗

@@ -5,7 +5,7 @@ use collections::HashMap;
 use pathfinder_geometry::rect::RectF;
 use smallvec::SmallVec;
 
-use crate::{platform::MouseButton, EventContext};
+use crate::{platform::MouseButton, window::WindowContext, View, ViewContext};
 
 use super::{
     mouse_event::{
@@ -60,82 +60,92 @@ impl MouseRegion {
         }
     }
 
-    pub fn on_down(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(MouseDown, &mut EventContext) + 'static,
-    ) -> Self {
+    pub fn on_down<V, F>(mut self, button: MouseButton, handler: F) -> Self
+    where
+        V: View,
+        F: Fn(MouseDown, &mut V, &mut ViewContext<V>) + 'static,
+    {
         self.handlers = self.handlers.on_down(button, handler);
         self
     }
 
-    pub fn on_up(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(MouseUp, &mut EventContext) + 'static,
-    ) -> Self {
+    pub fn on_up<V, F>(mut self, button: MouseButton, handler: F) -> Self
+    where
+        V: View,
+        F: Fn(MouseUp, &mut V, &mut ViewContext<V>) + 'static,
+    {
         self.handlers = self.handlers.on_up(button, handler);
         self
     }
 
-    pub fn on_click(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(MouseClick, &mut EventContext) + 'static,
-    ) -> Self {
+    pub fn on_click<V, F>(mut self, button: MouseButton, handler: F) -> Self
+    where
+        V: View,
+        F: Fn(MouseClick, &mut V, &mut ViewContext<V>) + 'static,
+    {
         self.handlers = self.handlers.on_click(button, handler);
         self
     }
 
-    pub fn on_down_out(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(MouseDownOut, &mut EventContext) + 'static,
-    ) -> Self {
+    pub fn on_down_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
+    where
+        V: View,
+        F: Fn(MouseDownOut, &mut V, &mut ViewContext<V>) + 'static,
+    {
         self.handlers = self.handlers.on_down_out(button, handler);
         self
     }
 
-    pub fn on_up_out(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(MouseUpOut, &mut EventContext) + 'static,
-    ) -> Self {
+    pub fn on_up_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
+    where
+        V: View,
+        F: Fn(MouseUpOut, &mut V, &mut ViewContext<V>) + 'static,
+    {
         self.handlers = self.handlers.on_up_out(button, handler);
         self
     }
 
-    pub fn on_drag(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(MouseDrag, &mut EventContext) + 'static,
-    ) -> Self {
+    pub fn on_drag<V, F>(mut self, button: MouseButton, handler: F) -> Self
+    where
+        V: View,
+        F: Fn(MouseDrag, &mut V, &mut ViewContext<V>) + 'static,
+    {
         self.handlers = self.handlers.on_drag(button, handler);
         self
     }
 
-    pub fn on_hover(mut self, handler: impl Fn(MouseHover, &mut EventContext) + 'static) -> Self {
+    pub fn on_hover<V, F>(mut self, handler: F) -> Self
+    where
+        V: View,
+        F: Fn(&mut V, &mut ViewContext<V>) + 'static,
+    {
         self.handlers = self.handlers.on_hover(handler);
         self
     }
 
-    pub fn on_move(mut self, handler: impl Fn(MouseMove, &mut EventContext) + 'static) -> Self {
+    pub fn on_move<V, F>(mut self, handler: F) -> Self
+    where
+        V: View,
+        F: Fn(&mut V, &mut ViewContext<V>) + 'static,
+    {
         self.handlers = self.handlers.on_move(handler);
         self
     }
 
-    pub fn on_move_out(
-        mut self,
-        handler: impl Fn(MouseMoveOut, &mut EventContext) + 'static,
-    ) -> Self {
+    pub fn on_move_out<V, F>(mut self, handler: F) -> Self
+    where
+        V: View,
+        F: Fn(&mut V, &mut ViewContext<V>) + 'static,
+    {
         self.handlers = self.handlers.on_move_out(handler);
         self
     }
 
-    pub fn on_scroll(
-        mut self,
-        handler: impl Fn(MouseScrollWheel, &mut EventContext) + 'static,
-    ) -> Self {
+    pub fn on_scroll<V, F>(mut self, handler: F) -> Self
+    where
+        V: View,
+        F: Fn(&mut V, &mut ViewContext<V>) + 'static,
+    {
         self.handlers = self.handlers.on_scroll(handler);
         self
     }
@@ -186,7 +196,7 @@ impl MouseRegionId {
     }
 }
 
-pub type HandlerCallback = Rc<dyn Fn(MouseEvent, &mut EventContext)>;
+pub type HandlerCallback = Rc<dyn Fn(MouseEvent, &mut WindowContext)>;
 
 #[derive(Clone, PartialEq, Eq, Hash)]
 pub struct HandlerKey {
@@ -283,7 +293,11 @@ impl HandlerSet {
         }
     }
 
-    pub fn on_move(mut self, handler: impl Fn(MouseMove, &mut EventContext) + 'static) -> Self {
+    pub fn on_move<V, F>(mut self, handler: F) -> Self
+    where
+        V: View,
+        F: Fn(MouseMove, &mut V, &mut ViewContext<Self>) + 'static,
+    {
         self.insert(MouseEvent::move_disc(), None,
             Rc::new(move |region_event, cx| {
                 if let MouseEvent::Move(e) = region_event {
@@ -297,10 +311,11 @@ impl HandlerSet {
         self
     }
 
-    pub fn on_move_out(
-        mut self,
-        handler: impl Fn(MouseMoveOut, &mut EventContext) + 'static,
-    ) -> Self {
+    pub fn on_move_out<V, F>(mut self, handler: F) -> Self
+    where
+        V: View,
+        F: Fn(MouseMoveOut, &mut V, &mut ViewContext<V>) + 'static,
+    {
         self.insert(MouseEvent::move_out_disc(), None,
             Rc::new(move |region_event, cx| {
                 if let MouseEvent::MoveOut(e) = region_event {
@@ -314,11 +329,11 @@ impl HandlerSet {
         self
     }
 
-    pub fn on_down(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(MouseDown, &mut EventContext) + 'static,
-    ) -> Self {
+    pub fn on_down<V, F>(mut self, button: MouseButton, handler: F) -> Self
+    where
+        V: View,
+        F: Fn(MouseDown, &mut V, &mut ViewContext<V>) + 'static,
+    {
         self.insert(MouseEvent::down_disc(), Some(button),
             Rc::new(move |region_event, cx| {
                 if let MouseEvent::Down(e) = region_event {
@@ -332,11 +347,11 @@ impl HandlerSet {
         self
     }
 
-    pub fn on_up(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(MouseUp, &mut EventContext) + 'static,
-    ) -> Self {
+    pub fn on_up<V, F>(mut self, button: MouseButton, handler: F) -> Self
+    where
+        V: View,
+        F: Fn(MouseUp, &mut V, &mut ViewContext<V>) + 'static,
+    {
         self.insert(MouseEvent::up_disc(), Some(button),
             Rc::new(move |region_event, cx| {
                 if let MouseEvent::Up(e) = region_event {
@@ -350,11 +365,11 @@ impl HandlerSet {
         self
     }
 
-    pub fn on_click(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(MouseClick, &mut EventContext) + 'static,
-    ) -> Self {
+    pub fn on_click<V, F>(mut self, button: MouseButton, handler: F) -> Self
+    where
+        V: View,
+        F: Fn(MouseClick, &mut V, &mut ViewContext<V>) + 'static,
+    {
         self.insert(MouseEvent::click_disc(), Some(button),
             Rc::new(move |region_event, cx| {
                 if let MouseEvent::Click(e) = region_event {
@@ -368,11 +383,11 @@ impl HandlerSet {
         self
     }
 
-    pub fn on_down_out(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(MouseDownOut, &mut EventContext) + 'static,
-    ) -> Self {
+    pub fn on_down_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
+    where
+        V: View,
+        F: Fn(MouseDownOut, &mut V, &mut ViewContext<V>) + 'static,
+    {
         self.insert(MouseEvent::down_out_disc(), Some(button),
             Rc::new(move |region_event, cx| {
                 if let MouseEvent::DownOut(e) = region_event {
@@ -386,11 +401,11 @@ impl HandlerSet {
         self
     }
 
-    pub fn on_up_out(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(MouseUpOut, &mut EventContext) + 'static,
-    ) -> Self {
+    pub fn on_up_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
+    where
+        V: View,
+        F: Fn(MouseUpOut, &mut V, &mut ViewContext<V>) + 'static,
+    {
         self.insert(MouseEvent::up_out_disc(), Some(button),
             Rc::new(move |region_event, cx| {
                 if let MouseEvent::UpOut(e) = region_event {
@@ -404,11 +419,11 @@ impl HandlerSet {
         self
     }
 
-    pub fn on_drag(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(MouseDrag, &mut EventContext) + 'static,
-    ) -> Self {
+    pub fn on_drag<V, F>(mut self, button: MouseButton, handler: F) -> Self
+    where
+        V: View,
+        F: Fn(MouseDrag, &mut V, &mut ViewContext<V>) + 'static,
+    {
         self.insert(MouseEvent::drag_disc(), Some(button),
             Rc::new(move |region_event, cx| {
                 if let MouseEvent::Drag(e) = region_event {
@@ -422,7 +437,11 @@ impl HandlerSet {
         self
     }
 
-    pub fn on_hover(mut self, handler: impl Fn(MouseHover, &mut EventContext) + 'static) -> Self {
+    pub fn on_hover<V, F>(mut self, handler: F) -> Self
+    where
+        V: View,
+        F: Fn(MouseHover, &mut V, &mut ViewContext<V>) + 'static,
+    {
         self.insert(MouseEvent::hover_disc(), None,
             Rc::new(move |region_event, cx| {
                 if let MouseEvent::Hover(e) = region_event {
@@ -436,10 +455,11 @@ impl HandlerSet {
         self
     }
 
-    pub fn on_scroll(
-        mut self,
-        handler: impl Fn(MouseScrollWheel, &mut EventContext) + 'static,
-    ) -> Self {
+    pub fn on_scroll<V, F>(mut self, handler: F) -> Self
+    where
+        V: View,
+        F: Fn(MouseScrollWheel, &mut V, &mut ViewContext<V>) + 'static,
+    {
         self.insert(MouseEvent::scroll_wheel_disc(), None,
             Rc::new(move |region_event, cx| {
                 if let MouseEvent::ScrollWheel(e) = region_event {

crates/gpui/src/test.rs 🔗

@@ -19,8 +19,8 @@ use crate::{
     platform,
     platform::Platform,
     util::CwdBacktrace,
-    AppContext, Element, ElementBox, Entity, FontCache, Handle, RenderContext, Subscription,
-    TestAppContext, View,
+    AppContext, Element, ElementBox, Entity, FontCache, Handle, Subscription, TestAppContext, View,
+    ViewContext,
 };
 
 #[cfg(test)]
@@ -240,7 +240,7 @@ impl View for EmptyView {
         "empty view"
     }
 
-    fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox {
+    fn render(&mut self, _: &mut ViewContext<Self>) -> ElementBox<Self> {
         Element::boxed(Empty::new())
     }
 }

crates/gpui/src/text_layout.rs 🔗

@@ -9,7 +9,7 @@ use crate::{
     platform::FontSystem,
     scene,
     window::WindowContext,
-    AppContext, PaintContext, SceneBuilder,
+    SceneBuilder,
 };
 use ordered_float::OrderedFloat;
 use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
@@ -376,11 +376,12 @@ impl Line {
 
     pub fn paint_wrapped(
         &self,
+        scene: &SceneBuilder,
         origin: Vector2F,
         visible_bounds: RectF,
         line_height: f32,
         boundaries: impl IntoIterator<Item = ShapedBoundary>,
-        cx: &mut PaintContext,
+        cx: &mut WindowContext,
     ) {
         let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.;
         let baseline_origin = vec2f(0., padding_top + self.layout.ascent);
@@ -419,14 +420,14 @@ impl Line {
                 );
                 if glyph_bounds.intersects(visible_bounds) {
                     if glyph.is_emoji {
-                        cx.scene.push_image_glyph(scene::ImageGlyph {
+                        scene.push_image_glyph(scene::ImageGlyph {
                             font_id: run.font_id,
                             font_size: self.layout.font_size,
                             id: glyph.id,
                             origin: glyph_bounds.origin() + baseline_origin,
                         });
                     } else {
-                        cx.scene.push_glyph(scene::Glyph {
+                        scene.push_glyph(scene::Glyph {
                             font_id: run.font_id,
                             font_size: self.layout.font_size,
                             id: glyph.id,

crates/gpui/src/views/select.rs 🔗

@@ -1,13 +1,13 @@
 use serde::Deserialize;
 
 use crate::{
-    actions, elements::*, impl_actions, platform::MouseButton, AppContext, Entity, RenderContext,
-    View, ViewContext, WeakViewHandle,
+    actions, elements::*, impl_actions, platform::MouseButton, AppContext, Entity, View,
+    ViewContext, WeakViewHandle,
 };
 
 pub struct Select {
     handle: WeakViewHandle<Self>,
-    render_item: Box<dyn Fn(usize, ItemType, bool, &AppContext) -> ElementBox>,
+    render_item: Box<dyn Fn(usize, ItemType, bool, &AppContext) -> ElementBox<Self>>,
     selected_item_ix: usize,
     item_count: usize,
     is_open: bool,
@@ -41,7 +41,7 @@ pub fn init(cx: &mut AppContext) {
 }
 
 impl Select {
-    pub fn new<F: 'static + Fn(usize, ItemType, bool, &AppContext) -> ElementBox>(
+    pub fn new<F: 'static + Fn(usize, ItemType, bool, &AppContext) -> ElementBox<Self>>(
         item_count: usize,
         cx: &mut ViewContext<Self>,
         render_item: F,
@@ -92,7 +92,7 @@ impl View for Select {
         "Select"
     }
 
-    fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> ElementBox<Self> {
         if self.item_count == 0 {
             return Empty::new().boxed();
         }
@@ -106,16 +106,21 @@ impl View for Select {
             Default::default()
         };
         let mut result = Flex::column().with_child(
-            MouseEventHandler::<Header>::new(self.handle.id(), cx, |mouse_state, cx| {
-                Container::new((self.render_item)(
-                    self.selected_item_ix,
-                    ItemType::Header,
-                    mouse_state.hovered(),
-                    cx,
-                ))
-                .with_style(style.header)
-                .boxed()
-            })
+            MouseEventHandler::<Header>::new(
+                self.handle.id(),
+                self,
+                cx,
+                |mouse_state, this, cx| {
+                    Container::new((self.render_item)(
+                        self.selected_item_ix,
+                        ItemType::Header,
+                        mouse_state.hovered(),
+                        cx,
+                    ))
+                    .with_style(style.header)
+                    .boxed()
+                },
+            )
             .on_click(MouseButton::Left, move |_, cx| {
                 cx.dispatch_action(ToggleSelect)
             })
@@ -134,18 +139,23 @@ impl View for Select {
                                     let selected_item_ix = this.selected_item_ix;
                                     range.end = range.end.min(this.item_count);
                                     items.extend(range.map(|ix| {
-                                        MouseEventHandler::<Item>::new(ix, cx, |mouse_state, cx| {
-                                            (this.render_item)(
-                                                ix,
-                                                if ix == selected_item_ix {
-                                                    ItemType::Selected
-                                                } else {
-                                                    ItemType::Unselected
-                                                },
-                                                mouse_state.hovered(),
-                                                cx,
-                                            )
-                                        })
+                                        MouseEventHandler::<Item>::new(
+                                            ix,
+                                            self,
+                                            cx,
+                                            |mouse_state, this, cx| {
+                                                (this.render_item)(
+                                                    ix,
+                                                    if ix == selected_item_ix {
+                                                        ItemType::Selected
+                                                    } else {
+                                                        ItemType::Unselected
+                                                    },
+                                                    mouse_state.hovered(),
+                                                    cx,
+                                                )
+                                            },
+                                        )
                                         .on_click(MouseButton::Left, move |_, cx| {
                                             cx.dispatch_action(SelectItem(ix))
                                         })