Merge remote-tracking branch 'origin/main' into jk

Conrad Irwin created

Change summary

crates/diagnostics/src/diagnostics.rs        |   37 
crates/editor/src/display_map/block_map.rs   |   11 
crates/editor/src/editor.rs                  |   27 
crates/editor/src/element.rs                 |  186 ++-
crates/gpui/src/app/test_context.rs          |    7 
crates/gpui/src/element.rs                   |   63 
crates/gpui/src/elements/canvas.rs           |   10 
crates/gpui/src/elements/div.rs              |   61 
crates/gpui/src/elements/img.rs              |    8 
crates/gpui/src/elements/list.rs             |   10 
crates/gpui/src/elements/overlay.rs          |    8 
crates/gpui/src/elements/svg.rs              |    8 
crates/gpui/src/elements/text.rs             |   27 
crates/gpui/src/elements/uniform_list.rs     |    8 
crates/gpui/src/gpui.rs                      |   19 
crates/gpui/src/key_dispatch.rs              |    6 
crates/gpui/src/style.rs                     |   61 -
crates/gpui/src/text_system/line.rs          |   10 
crates/gpui/src/view.rs                      |   26 
crates/gpui/src/window.rs                    |  957 +-----------------
crates/gpui/src/window/element_cx.rs         | 1129 ++++++++++++++++++++++
crates/terminal_view/src/terminal_element.rs |   24 
crates/ui/src/components/popover_menu.rs     |    9 
crates/ui/src/components/right_click_menu.rs |   10 
crates/ui/src/prelude.rs                     |    6 
crates/vim/src/test.rs                       |    4 
crates/workspace/src/pane_group.rs           |    6 
crates/workspace/src/workspace.rs            |   32 
28 files changed, 1,547 insertions(+), 1,223 deletions(-)

Detailed changes

crates/diagnostics/src/diagnostics.rs 🔗

@@ -1584,27 +1584,34 @@ mod tests {
     }
 
     fn editor_blocks(editor: &View<Editor>, cx: &mut WindowContext) -> Vec<(u32, SharedString)> {
+        let editor_view = editor.clone();
         editor.update(cx, |editor, cx| {
             let snapshot = editor.snapshot(cx);
             snapshot
                 .blocks_in_range(0..snapshot.max_point().row())
                 .enumerate()
                 .filter_map(|(ix, (row, block))| {
-                    let name = match block {
-                        TransformBlock::Custom(block) => block
-                            .render(&mut BlockContext {
-                                view_context: cx,
-                                anchor_x: px(0.),
-                                gutter_padding: px(0.),
-                                gutter_width: px(0.),
-                                line_height: px(0.),
-                                em_width: px(0.),
-                                block_id: ix,
-                                editor_style: &editor::EditorStyle::default(),
-                            })
-                            .inner_id()?
-                            .try_into()
-                            .ok()?,
+                    let name: SharedString = match block {
+                        TransformBlock::Custom(block) => cx.with_element_context({
+                            let editor_view = editor_view.clone();
+                            |cx| -> Option<SharedString> {
+                                block
+                                    .render(&mut BlockContext {
+                                        context: cx,
+                                        anchor_x: px(0.),
+                                        gutter_padding: px(0.),
+                                        gutter_width: px(0.),
+                                        line_height: px(0.),
+                                        em_width: px(0.),
+                                        block_id: ix,
+                                        view: editor_view,
+                                        editor_style: &editor::EditorStyle::default(),
+                                    })
+                                    .inner_id()?
+                                    .try_into()
+                                    .ok()
+                            }
+                        })?,
 
                         TransformBlock::ExcerptHeader {
                             starts_new_buffer, ..

crates/editor/src/display_map/block_map.rs 🔗

@@ -4,7 +4,7 @@ use super::{
 };
 use crate::{Anchor, Editor, EditorStyle, ExcerptId, ExcerptRange, ToPoint as _};
 use collections::{Bound, HashMap, HashSet};
-use gpui::{AnyElement, Pixels, ViewContext};
+use gpui::{AnyElement, ElementContext, Pixels, View};
 use language::{BufferSnapshot, Chunk, Patch, Point};
 use parking_lot::Mutex;
 use std::{
@@ -81,7 +81,8 @@ pub enum BlockStyle {
 }
 
 pub struct BlockContext<'a, 'b> {
-    pub view_context: &'b mut ViewContext<'a, Editor>,
+    pub context: &'b mut ElementContext<'a>,
+    pub view: View<Editor>,
     pub anchor_x: Pixels,
     pub gutter_width: Pixels,
     pub gutter_padding: Pixels,
@@ -933,16 +934,16 @@ impl BlockDisposition {
 }
 
 impl<'a> Deref for BlockContext<'a, '_> {
-    type Target = ViewContext<'a, Editor>;
+    type Target = ElementContext<'a>;
 
     fn deref(&self) -> &Self::Target {
-        self.view_context
+        self.context
     }
 }
 
 impl DerefMut for BlockContext<'_, '_> {
     fn deref_mut(&mut self) -> &mut Self::Target {
-        self.view_context
+        self.context
     }
 }
 

crates/editor/src/editor.rs 🔗

@@ -3912,7 +3912,7 @@ impl Editor {
         gutter_hovered: bool,
         _line_height: Pixels,
         _gutter_margin: Pixels,
-        cx: &mut ViewContext<Self>,
+        editor_view: View<Editor>,
     ) -> Vec<Option<IconButton>> {
         fold_data
             .iter()
@@ -3922,14 +3922,19 @@ impl Editor {
                     .map(|(fold_status, buffer_row, active)| {
                         (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
                             IconButton::new(ix as usize, ui::IconName::ChevronDown)
-                                .on_click(cx.listener(move |editor, _e, cx| match fold_status {
-                                    FoldStatus::Folded => {
-                                        editor.unfold_at(&UnfoldAt { buffer_row }, cx);
-                                    }
-                                    FoldStatus::Foldable => {
-                                        editor.fold_at(&FoldAt { buffer_row }, cx);
+                                .on_click({
+                                    let view = editor_view.clone();
+                                    move |_e, cx| {
+                                        view.update(cx, |editor, cx| match fold_status {
+                                            FoldStatus::Folded => {
+                                                editor.unfold_at(&UnfoldAt { buffer_row }, cx);
+                                            }
+                                            FoldStatus::Foldable => {
+                                                editor.fold_at(&FoldAt { buffer_row }, cx);
+                                            }
+                                        })
                                     }
-                                }))
+                                })
                                 .icon_color(ui::Color::Muted)
                                 .icon_size(ui::IconSize::Small)
                                 .selected(fold_status == FoldStatus::Folded)
@@ -9575,10 +9580,10 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, _is_valid: bool) -> Ren
                     .size(ButtonSize::Compact)
                     .style(ButtonStyle::Transparent)
                     .visible_on_hover(group_id)
-                    .on_click(cx.listener({
+                    .on_click({
                         let message = diagnostic.message.clone();
-                        move |_, _, cx| cx.write_to_clipboard(ClipboardItem::new(message.clone()))
-                    }))
+                        move |_click, cx| cx.write_to_clipboard(ClipboardItem::new(message.clone()))
+                    })
                     .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
             )
             .into_any_element()

crates/editor/src/element.rs 🔗

@@ -25,12 +25,12 @@ use collections::{BTreeMap, HashMap};
 use git::diff::DiffHunkStatus;
 use gpui::{
     div, fill, outline, overlay, point, px, quad, relative, size, transparent_black, Action,
-    AnchorCorner, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Corners,
-    CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Entity, Hsla,
-    InteractiveBounds, InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton,
-    MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, ScrollDelta,
-    ScrollWheelEvent, ShapedLine, SharedString, Size, StackingOrder, StatefulInteractiveElement,
-    Style, Styled, TextRun, TextStyle, View, ViewContext, WindowContext,
+    AnchorCorner, AnyElement, AvailableSpace, Bounds, ContentMask, Corners, CursorStyle,
+    DispatchPhase, Edges, Element, ElementInputHandler, Entity, Hsla, InteractiveBounds,
+    InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton, MouseDownEvent,
+    MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine,
+    SharedString, Size, StackingOrder, StatefulInteractiveElement, Style, Styled, TextRun,
+    TextStyle, View, ViewContext, WindowContext,
 };
 use itertools::Itertools;
 use language::language_settings::ShowWhitespaceSetting;
@@ -330,7 +330,7 @@ impl EditorElement {
         register_action(view, cx, Editor::display_cursor_names);
     }
 
-    fn register_key_listeners(&self, cx: &mut WindowContext) {
+    fn register_key_listeners(&self, cx: &mut ElementContext) {
         cx.on_key_event({
             let editor = self.editor.clone();
             move |event: &ModifiersChangedEvent, phase, cx| {
@@ -628,7 +628,7 @@ impl EditorElement {
         gutter_bounds: Bounds<Pixels>,
         text_bounds: Bounds<Pixels>,
         layout: &LayoutState,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let bounds = gutter_bounds.union(&text_bounds);
         let scroll_top =
@@ -711,7 +711,7 @@ impl EditorElement {
         &mut self,
         bounds: Bounds<Pixels>,
         layout: &mut LayoutState,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let line_height = layout.position_map.line_height;
 
@@ -782,7 +782,7 @@ impl EditorElement {
         });
     }
 
-    fn paint_diff_hunks(bounds: Bounds<Pixels>, layout: &LayoutState, cx: &mut WindowContext) {
+    fn paint_diff_hunks(bounds: Bounds<Pixels>, layout: &LayoutState, cx: &mut ElementContext) {
         let line_height = layout.position_map.line_height;
 
         let scroll_position = layout.position_map.snapshot.scroll_position();
@@ -886,7 +886,7 @@ impl EditorElement {
         &mut self,
         text_bounds: Bounds<Pixels>,
         layout: &mut LayoutState,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let start_row = layout.visible_display_row_range.start;
         let content_origin = text_bounds.origin + point(layout.gutter_margin, Pixels::ZERO);
@@ -1153,7 +1153,7 @@ impl EditorElement {
         &mut self,
         text_bounds: Bounds<Pixels>,
         layout: &mut LayoutState,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let content_origin = text_bounds.origin + point(layout.gutter_margin, Pixels::ZERO);
         let start_row = layout.visible_display_row_range.start;
@@ -1268,7 +1268,7 @@ impl EditorElement {
         &mut self,
         bounds: Bounds<Pixels>,
         layout: &mut LayoutState,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         if layout.mode != EditorMode::Full {
             return;
@@ -1512,7 +1512,7 @@ impl EditorElement {
         layout: &LayoutState,
         content_origin: gpui::Point<Pixels>,
         bounds: Bounds<Pixels>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let start_row = layout.visible_display_row_range.start;
         let end_row = layout.visible_display_row_range.end;
@@ -1564,7 +1564,7 @@ impl EditorElement {
         &mut self,
         bounds: Bounds<Pixels>,
         layout: &mut LayoutState,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let scroll_position = layout.position_map.snapshot.scroll_position();
         let scroll_left = scroll_position.x * layout.position_map.em_width;
@@ -1814,7 +1814,7 @@ impl EditorElement {
         }
     }
 
-    fn compute_layout(&mut self, bounds: Bounds<Pixels>, cx: &mut WindowContext) -> LayoutState {
+    fn compute_layout(&mut self, bounds: Bounds<Pixels>, cx: &mut ElementContext) -> LayoutState {
         self.editor.update(cx, |editor, cx| {
             let snapshot = editor.snapshot(cx);
             let style = self.style.clone();
@@ -2083,7 +2083,9 @@ impl EditorElement {
                 .width;
             let scroll_width = longest_line_width.max(max_visible_line_width) + overscroll.width;
 
-            let (scroll_width, blocks) = cx.with_element_id(Some("editor_blocks"), |cx| {
+            let editor_view = cx.view().clone();
+            let (scroll_width, blocks) = cx.with_element_context(|cx| {
+             cx.with_element_id(Some("editor_blocks"), |cx| {
                 self.layout_blocks(
                     start_row..end_row,
                     &snapshot,
@@ -2097,8 +2099,10 @@ impl EditorElement {
                     &style,
                     &line_layouts,
                     editor,
+                    editor_view,
                     cx,
                 )
+            })
             });
 
             let scroll_max = point(
@@ -2174,15 +2178,19 @@ impl EditorElement {
                 cx,
             );
 
-            let fold_indicators = cx.with_element_id(Some("gutter_fold_indicators"), |cx| {
+            let editor_view = cx.view().clone();
+            let fold_indicators = cx.with_element_context(|cx| {
+
+                cx.with_element_id(Some("gutter_fold_indicators"), |_cx| {
                 editor.render_fold_indicators(
                     fold_statuses,
                     &style,
                     editor.gutter_hovered,
                     line_height,
                     gutter_margin,
-                    cx,
+                    editor_view,
                 )
+            })
             });
 
             let invisible_symbol_font_size = font_size / 2.;
@@ -2273,7 +2281,8 @@ impl EditorElement {
         style: &EditorStyle,
         line_layouts: &[LineWithInvisibles],
         editor: &mut Editor,
-        cx: &mut ViewContext<Editor>,
+        editor_view: View<Editor>,
+        cx: &mut ElementContext,
     ) -> (Pixels, Vec<BlockLayout>) {
         let mut block_id = 0;
         let (fixed_blocks, non_fixed_blocks) = snapshot
@@ -2287,7 +2296,7 @@ impl EditorElement {
                             available_space: Size<AvailableSpace>,
                             block_id: usize,
                             editor: &mut Editor,
-                            cx: &mut ViewContext<Editor>| {
+                            cx: &mut ElementContext| {
             let mut element = match block {
                 TransformBlock::Custom(block) => {
                     let align_to = block
@@ -2306,13 +2315,14 @@ impl EditorElement {
                         };
 
                     block.render(&mut BlockContext {
-                        view_context: cx,
+                        context: cx,
                         anchor_x,
                         gutter_padding,
                         line_height,
                         gutter_width,
                         em_width,
                         block_id,
+                        view: editor_view.clone(),
                         editor_style: &self.style,
                     })
                 }
@@ -2504,7 +2514,7 @@ impl EditorElement {
         &mut self,
         interactive_bounds: &InteractiveBounds,
         layout: &LayoutState,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         cx.on_mouse_event({
             let position_map = layout.position_map.clone();
@@ -2564,7 +2574,7 @@ impl EditorElement {
         gutter_bounds: Bounds<Pixels>,
         text_bounds: Bounds<Pixels>,
         layout: &LayoutState,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let interactive_bounds = InteractiveBounds {
             bounds: bounds.intersect(&cx.content_mask().bounds),
@@ -2787,7 +2797,7 @@ impl LineWithInvisibles {
         content_origin: gpui::Point<Pixels>,
         whitespace_setting: ShowWhitespaceSetting,
         selection_ranges: &[Range<DisplayPoint>],
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let line_height = layout.position_map.line_height;
         let line_y = line_height * row as f32 - layout.position_map.scroll_position.y;
@@ -2821,7 +2831,7 @@ impl LineWithInvisibles {
         row: u32,
         line_height: Pixels,
         whitespace_setting: ShowWhitespaceSetting,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let allowed_invisibles_regions = match whitespace_setting {
             ShowWhitespaceSetting::None => return,
@@ -2870,7 +2880,7 @@ impl Element for EditorElement {
     fn request_layout(
         &mut self,
         _element_state: Option<Self::State>,
-        cx: &mut gpui::WindowContext,
+        cx: &mut gpui::ElementContext,
     ) -> (gpui::LayoutId, Self::State) {
         cx.with_view_id(self.editor.entity_id(), |cx| {
             self.editor.update(cx, |editor, cx| {
@@ -2882,34 +2892,36 @@ impl Element for EditorElement {
                         let mut style = Style::default();
                         style.size.width = relative(1.).into();
                         style.size.height = self.style.text.line_height_in_pixels(rem_size).into();
-                        cx.request_layout(&style, None)
+                        cx.with_element_context(|cx| cx.request_layout(&style, None))
                     }
                     EditorMode::AutoHeight { max_lines } => {
                         let editor_handle = cx.view().clone();
                         let max_line_number_width =
                             self.max_line_number_width(&editor.snapshot(cx), cx);
-                        cx.request_measured_layout(
-                            Style::default(),
-                            move |known_dimensions, _, cx| {
-                                editor_handle
-                                    .update(cx, |editor, cx| {
-                                        compute_auto_height_layout(
-                                            editor,
-                                            max_lines,
-                                            max_line_number_width,
-                                            known_dimensions,
-                                            cx,
-                                        )
-                                    })
-                                    .unwrap_or_default()
-                            },
-                        )
+                        cx.with_element_context(|cx| {
+                            cx.request_measured_layout(
+                                Style::default(),
+                                move |known_dimensions, _, cx| {
+                                    editor_handle
+                                        .update(cx, |editor, cx| {
+                                            compute_auto_height_layout(
+                                                editor,
+                                                max_lines,
+                                                max_line_number_width,
+                                                known_dimensions,
+                                                cx,
+                                            )
+                                        })
+                                        .unwrap_or_default()
+                                },
+                            )
+                        })
                     }
                     EditorMode::Full => {
                         let mut style = Style::default();
                         style.size.width = relative(1.).into();
                         style.size.height = relative(1.).into();
-                        cx.request_layout(&style, None)
+                        cx.with_element_context(|cx| cx.request_layout(&style, None))
                     }
                 };
 
@@ -2922,7 +2934,7 @@ impl Element for EditorElement {
         &mut self,
         bounds: Bounds<gpui::Pixels>,
         _element_state: &mut Self::State,
-        cx: &mut gpui::WindowContext,
+        cx: &mut gpui::ElementContext,
     ) {
         let editor = self.editor.clone();
 
@@ -3204,7 +3216,7 @@ impl Cursor {
         }
     }
 
-    pub fn paint(&self, origin: gpui::Point<Pixels>, cx: &mut WindowContext) {
+    pub fn paint(&self, origin: gpui::Point<Pixels>, cx: &mut ElementContext) {
         let bounds = match self.shape {
             CursorShape::Bar => Bounds {
                 origin: self.origin + origin,
@@ -3284,7 +3296,7 @@ pub struct HighlightedRangeLine {
 }
 
 impl HighlightedRange {
-    pub fn paint(&self, bounds: Bounds<Pixels>, cx: &mut WindowContext) {
+    pub fn paint(&self, bounds: Bounds<Pixels>, cx: &mut ElementContext) {
         if self.lines.len() >= 2 && self.lines[0].start_x > self.lines[1].end_x {
             self.paint_lines(self.start_y, &self.lines[0..1], bounds, cx);
             self.paint_lines(
@@ -3303,7 +3315,7 @@ impl HighlightedRange {
         start_y: Pixels,
         lines: &[HighlightedRangeLine],
         _bounds: Bounds<Pixels>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         if lines.is_empty() {
             return;
@@ -3521,14 +3533,16 @@ mod tests {
             .unwrap();
         let state = cx
             .update_window(window.into(), |view, cx| {
-                cx.with_view_id(view.entity_id(), |cx| {
-                    element.compute_layout(
-                        Bounds {
-                            origin: point(px(500.), px(500.)),
-                            size: size(px(500.), px(500.)),
-                        },
-                        cx,
-                    )
+                cx.with_element_context(|cx| {
+                    cx.with_view_id(view.entity_id(), |cx| {
+                        element.compute_layout(
+                            Bounds {
+                                origin: point(px(500.), px(500.)),
+                                size: size(px(500.), px(500.)),
+                            },
+                            cx,
+                        )
+                    })
                 })
             })
             .unwrap();
@@ -3615,14 +3629,16 @@ mod tests {
 
         let state = cx
             .update_window(window.into(), |view, cx| {
-                cx.with_view_id(view.entity_id(), |cx| {
-                    element.compute_layout(
-                        Bounds {
-                            origin: point(px(500.), px(500.)),
-                            size: size(px(500.), px(500.)),
-                        },
-                        cx,
-                    )
+                cx.with_element_context(|cx| {
+                    cx.with_view_id(view.entity_id(), |cx| {
+                        element.compute_layout(
+                            Bounds {
+                                origin: point(px(500.), px(500.)),
+                                size: size(px(500.), px(500.)),
+                            },
+                            cx,
+                        )
+                    })
                 })
             })
             .unwrap();
@@ -3679,14 +3695,16 @@ mod tests {
         let mut element = EditorElement::new(&editor, style);
         let state = cx
             .update_window(window.into(), |view, cx| {
-                cx.with_view_id(view.entity_id(), |cx| {
-                    element.compute_layout(
-                        Bounds {
-                            origin: point(px(500.), px(500.)),
-                            size: size(px(500.), px(500.)),
-                        },
-                        cx,
-                    )
+                cx.with_element_context(|cx| {
+                    cx.with_view_id(view.entity_id(), |cx| {
+                        element.compute_layout(
+                            Bounds {
+                                origin: point(px(500.), px(500.)),
+                                size: size(px(500.), px(500.)),
+                            },
+                            cx,
+                        )
+                    })
                 })
             })
             .unwrap();
@@ -3704,8 +3722,10 @@ mod tests {
 
         // Don't panic.
         let bounds = Bounds::<Pixels>::new(Default::default(), size);
-        cx.update_window(window.into(), |_, cx| element.paint(bounds, &mut (), cx))
-            .unwrap()
+        cx.update_window(window.into(), |_, cx| {
+            cx.with_element_context(|cx| element.paint(bounds, &mut (), cx))
+        })
+        .unwrap()
     }
 
     #[gpui::test]
@@ -3880,13 +3900,15 @@ mod tests {
             .unwrap();
         let layout_state = cx
             .update_window(window.into(), |_, cx| {
-                element.compute_layout(
-                    Bounds {
-                        origin: point(px(500.), px(500.)),
-                        size: size(px(500.), px(500.)),
-                    },
-                    cx,
-                )
+                cx.with_element_context(|cx| {
+                    element.compute_layout(
+                        Bounds {
+                            origin: point(px(500.), px(500.)),
+                            size: size(px(500.), px(500.)),
+                        },
+                        cx,
+                    )
+                })
             })
             .unwrap();
 

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

@@ -640,8 +640,11 @@ impl<'a> VisualTestContext {
                 .as_ref()
                 .expect("Can't draw to this window without a root view")
                 .entity_id();
-            cx.with_view_id(entity_id, |cx| {
-                f(cx).draw(origin, space, cx);
+
+            cx.with_element_context(|cx| {
+                cx.with_view_id(entity_id, |cx| {
+                    f(cx).draw(origin, space, cx);
+                })
             });
 
             cx.refresh();

crates/gpui/src/element.rs 🔗

@@ -35,12 +35,12 @@
 //! your own custom layout algorithm or rendering a code editor.
 
 use crate::{
-    util::FluentBuilder, ArenaBox, AvailableSpace, BorrowWindow, Bounds, ElementId, LayoutId,
+    util::FluentBuilder, ArenaBox, AvailableSpace, Bounds, ElementContext, ElementId, LayoutId,
     Pixels, Point, Size, ViewContext, WindowContext, ELEMENT_ARENA,
 };
 use derive_more::{Deref, DerefMut};
 pub(crate) use smallvec::SmallVec;
-use std::{any::Any, fmt::Debug};
+use std::{any::Any, fmt::Debug, ops::DerefMut};
 
 /// Implemented by types that participate in laying out and painting the contents of a window.
 /// Elements form a tree and are laid out according to web-based layout rules, as implemented by Taffy.
@@ -56,12 +56,12 @@ pub trait Element: 'static + IntoElement {
     fn request_layout(
         &mut self,
         state: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State);
 
     /// Once layout has been completed, this method will be called to paint the element to the screen.
     /// The state argument is the same state that was returned from [`Element::request_layout()`].
-    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext);
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext);
 
     /// Convert this element into a dynamically-typed [`AnyElement`].
     fn into_any(self) -> AnyElement {
@@ -95,8 +95,8 @@ pub trait IntoElement: Sized {
         self,
         origin: Point<Pixels>,
         available_space: Size<T>,
-        cx: &mut WindowContext,
-        f: impl FnOnce(&mut <Self::Element as Element>::State, &mut WindowContext) -> R,
+        cx: &mut ElementContext,
+        f: impl FnOnce(&mut <Self::Element as Element>::State, &mut ElementContext) -> R,
     ) -> R
     where
         T: Clone + Default + Debug + Into<AvailableSpace>,
@@ -193,14 +193,19 @@ impl<C: RenderOnce> Element for Component<C> {
     fn request_layout(
         &mut self,
         _: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
-        let mut element = self.0.take().unwrap().render(cx).into_any_element();
+        let mut element = self
+            .0
+            .take()
+            .unwrap()
+            .render(cx.deref_mut())
+            .into_any_element();
         let layout_id = element.request_layout(cx);
         (layout_id, element)
     }
 
-    fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut WindowContext) {
+    fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut ElementContext) {
         element.paint(cx)
     }
 }
@@ -224,21 +229,21 @@ pub(crate) struct GlobalElementId(SmallVec<[ElementId; 32]>);
 trait ElementObject {
     fn element_id(&self) -> Option<ElementId>;
 
-    fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId;
+    fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId;
 
-    fn paint(&mut self, cx: &mut WindowContext);
+    fn paint(&mut self, cx: &mut ElementContext);
 
     fn measure(
         &mut self,
         available_space: Size<AvailableSpace>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> Size<Pixels>;
 
     fn draw(
         &mut self,
         origin: Point<Pixels>,
         available_space: Size<AvailableSpace>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     );
 }
 
@@ -276,7 +281,7 @@ impl<E: Element> DrawableElement<E> {
         self.element.as_ref()?.element_id()
     }
 
-    fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
+    fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
         let (layout_id, frame_state) = if let Some(id) = self.element.as_ref().unwrap().element_id()
         {
             let layout_id = cx.with_element_state(id, |element_state, cx| {
@@ -298,7 +303,7 @@ impl<E: Element> DrawableElement<E> {
         layout_id
     }
 
-    fn paint(mut self, cx: &mut WindowContext) -> Option<E::State> {
+    fn paint(mut self, cx: &mut ElementContext) -> Option<E::State> {
         match self.phase {
             ElementDrawPhase::LayoutRequested {
                 layout_id,
@@ -343,7 +348,7 @@ impl<E: Element> DrawableElement<E> {
     fn measure(
         &mut self,
         available_space: Size<AvailableSpace>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> Size<Pixels> {
         if matches!(&self.phase, ElementDrawPhase::Start) {
             self.request_layout(cx);
@@ -384,7 +389,7 @@ impl<E: Element> DrawableElement<E> {
         mut self,
         origin: Point<Pixels>,
         available_space: Size<AvailableSpace>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> Option<E::State> {
         self.measure(available_space, cx);
         cx.with_absolute_element_offset(origin, |cx| self.paint(cx))
@@ -400,18 +405,18 @@ where
         self.as_ref().unwrap().element_id()
     }
 
-    fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
+    fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
         DrawableElement::request_layout(self.as_mut().unwrap(), cx)
     }
 
-    fn paint(&mut self, cx: &mut WindowContext) {
+    fn paint(&mut self, cx: &mut ElementContext) {
         DrawableElement::paint(self.take().unwrap(), cx);
     }
 
     fn measure(
         &mut self,
         available_space: Size<AvailableSpace>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> Size<Pixels> {
         DrawableElement::measure(self.as_mut().unwrap(), available_space, cx)
     }
@@ -420,7 +425,7 @@ where
         &mut self,
         origin: Point<Pixels>,
         available_space: Size<AvailableSpace>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         DrawableElement::draw(self.take().unwrap(), origin, available_space, cx);
     }
@@ -443,12 +448,12 @@ impl AnyElement {
 
     /// Request the layout ID of the element stored in this `AnyElement`.
     /// Used for laying out child elements in a parent element.
-    pub fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
+    pub fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
         self.0.request_layout(cx)
     }
 
     /// Paints the element stored in this `AnyElement`.
-    pub fn paint(&mut self, cx: &mut WindowContext) {
+    pub fn paint(&mut self, cx: &mut ElementContext) {
         self.0.paint(cx)
     }
 
@@ -456,7 +461,7 @@ impl AnyElement {
     pub fn measure(
         &mut self,
         available_space: Size<AvailableSpace>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> Size<Pixels> {
         self.0.measure(available_space, cx)
     }
@@ -466,7 +471,7 @@ impl AnyElement {
         &mut self,
         origin: Point<Pixels>,
         available_space: Size<AvailableSpace>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         self.0.draw(origin, available_space, cx)
     }
@@ -483,13 +488,13 @@ impl Element for AnyElement {
     fn request_layout(
         &mut self,
         _: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         let layout_id = self.request_layout(cx);
         (layout_id, ())
     }
 
-    fn paint(&mut self, _: Bounds<Pixels>, _: &mut Self::State, cx: &mut WindowContext) {
+    fn paint(&mut self, _: Bounds<Pixels>, _: &mut Self::State, cx: &mut ElementContext) {
         self.paint(cx)
     }
 }
@@ -531,7 +536,7 @@ impl Element for () {
     fn request_layout(
         &mut self,
         _state: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         (cx.request_layout(&crate::Style::default(), None), ())
     }
@@ -540,7 +545,7 @@ impl Element for () {
         &mut self,
         _bounds: Bounds<Pixels>,
         _state: &mut Self::State,
-        _cx: &mut WindowContext,
+        _cx: &mut ElementContext,
     ) {
     }
 }

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

@@ -1,10 +1,10 @@
 use refineable::Refineable as _;
 
-use crate::{Bounds, Element, IntoElement, Pixels, Style, StyleRefinement, Styled, WindowContext};
+use crate::{Bounds, Element, ElementContext, IntoElement, Pixels, Style, StyleRefinement, Styled};
 
 /// Construct a canvas element with the given paint callback.
 /// Useful for adding short term custom drawing to a view.
-pub fn canvas(callback: impl 'static + FnOnce(&Bounds<Pixels>, &mut WindowContext)) -> Canvas {
+pub fn canvas(callback: impl 'static + FnOnce(&Bounds<Pixels>, &mut ElementContext)) -> Canvas {
     Canvas {
         paint_callback: Some(Box::new(callback)),
         style: StyleRefinement::default(),
@@ -14,7 +14,7 @@ pub fn canvas(callback: impl 'static + FnOnce(&Bounds<Pixels>, &mut WindowContex
 /// A canvas element, meant for accessing the low level paint API without defining a whole
 /// custom element
 pub struct Canvas {
-    paint_callback: Option<Box<dyn FnOnce(&Bounds<Pixels>, &mut WindowContext)>>,
+    paint_callback: Option<Box<dyn FnOnce(&Bounds<Pixels>, &mut ElementContext)>>,
     style: StyleRefinement,
 }
 
@@ -36,7 +36,7 @@ impl Element for Canvas {
     fn request_layout(
         &mut self,
         _: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (crate::LayoutId, Self::State) {
         let mut style = Style::default();
         style.refine(&self.style);
@@ -44,7 +44,7 @@ impl Element for Canvas {
         (layout_id, style)
     }
 
-    fn paint(&mut self, bounds: Bounds<Pixels>, style: &mut Style, cx: &mut WindowContext) {
+    fn paint(&mut self, bounds: Bounds<Pixels>, style: &mut Style, cx: &mut ElementContext) {
         style.paint(bounds, cx, |cx| {
             (self.paint_callback.take().unwrap())(&bounds, cx)
         });

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

@@ -23,12 +23,11 @@
 //!
 
 use crate::{
-    point, px, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, BorrowAppContext,
-    BorrowWindow, Bounds, ClickEvent, DispatchPhase, Element, ElementId, FocusHandle, IntoElement,
-    IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent,
-    MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Render, ScrollWheelEvent,
-    SharedString, Size, StackingOrder, Style, StyleRefinement, Styled, Task, View, Visibility,
-    WindowContext,
+    point, px, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, Bounds, ClickEvent,
+    DispatchPhase, Element, ElementContext, ElementId, FocusHandle, IntoElement, IsZero,
+    KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent, MouseMoveEvent,
+    MouseUpEvent, ParentElement, Pixels, Point, Render, ScrollWheelEvent, SharedString, Size,
+    StackingOrder, Style, StyleRefinement, Styled, Task, View, Visibility, WindowContext,
 };
 
 use collections::HashMap;
@@ -41,6 +40,7 @@ use std::{
     fmt::Debug,
     marker::PhantomData,
     mem,
+    ops::DerefMut,
     rc::Rc,
     time::Duration,
 };
@@ -1052,7 +1052,7 @@ impl Element for Div {
     fn request_layout(
         &mut self,
         element_state: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         let mut child_layout_ids = SmallVec::new();
         let (layout_id, interactive_state) = self.interactivity.layout(
@@ -1082,7 +1082,7 @@ impl Element for Div {
         &mut self,
         bounds: Bounds<Pixels>,
         element_state: &mut Self::State,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let mut child_min = point(Pixels::MAX, Pixels::MAX);
         let mut child_max = Point::default();
@@ -1233,8 +1233,8 @@ impl Interactivity {
     pub fn layout(
         &mut self,
         element_state: Option<InteractiveElementState>,
-        cx: &mut WindowContext,
-        f: impl FnOnce(Style, &mut WindowContext) -> LayoutId,
+        cx: &mut ElementContext,
+        f: impl FnOnce(Style, &mut ElementContext) -> LayoutId,
     ) -> (LayoutId, InteractiveElementState) {
         let mut element_state = element_state.unwrap_or_default();
 
@@ -1281,8 +1281,8 @@ impl Interactivity {
         bounds: Bounds<Pixels>,
         content_size: Size<Pixels>,
         element_state: &mut InteractiveElementState,
-        cx: &mut WindowContext,
-        f: impl FnOnce(&Style, Point<Pixels>, &mut WindowContext),
+        cx: &mut ElementContext,
+        f: impl FnOnce(&Style, Point<Pixels>, &mut ElementContext),
     ) {
         let style = self.compute_style(Some(bounds), element_state, cx);
         let z_index = style.z_index.unwrap_or(0);
@@ -1295,7 +1295,7 @@ impl Interactivity {
                 .insert(debug_selector.clone(), bounds);
         }
 
-        let paint_hover_group_handler = |cx: &mut WindowContext| {
+        let paint_hover_group_handler = |cx: &mut ElementContext| {
             let hover_group_bounds = self
                 .group_hover_style
                 .as_ref()
@@ -1319,7 +1319,7 @@ impl Interactivity {
         }
 
         cx.with_z_index(z_index, |cx| {
-            style.paint(bounds, cx, |cx| {
+            style.paint(bounds, cx, |cx: &mut ElementContext| {
                 cx.with_text_style(style.text_style().cloned(), |cx| {
                     cx.with_content_mask(style.overflow_mask(bounds, cx.rem_size()), |cx| {
                         #[cfg(debug_assertions)]
@@ -1333,7 +1333,7 @@ impl Interactivity {
                             let element_id = format!("{:?}", self.element_id.as_ref().unwrap());
                             let str_len = element_id.len();
 
-                            let render_debug_text = |cx: &mut WindowContext| {
+                            let render_debug_text = |cx: &mut ElementContext| {
                                 if let Some(text) = cx
                                     .text_system()
                                     .shape_text(
@@ -1540,12 +1540,17 @@ impl Interactivity {
 
                                                     let mut can_drop = true;
                                                     if let Some(predicate) = &can_drop_predicate {
-                                                        can_drop =
-                                                            predicate(drag.value.as_ref(), cx);
+                                                        can_drop = predicate(
+                                                            drag.value.as_ref(),
+                                                            cx.deref_mut(),
+                                                        );
                                                     }
 
                                                     if can_drop {
-                                                        listener(drag.value.as_ref(), cx);
+                                                        listener(
+                                                            drag.value.as_ref(),
+                                                            cx.deref_mut(),
+                                                        );
                                                         cx.refresh();
                                                         cx.stop_propagation();
                                                     }
@@ -1676,7 +1681,7 @@ impl Interactivity {
                                     *was_hovered = is_hovered;
                                     drop(was_hovered);
 
-                                    hover_listener(&is_hovered, cx);
+                                    hover_listener(&is_hovered, cx.deref_mut());
                                 }
                             });
                         }
@@ -1897,7 +1902,7 @@ impl Interactivity {
         &self,
         bounds: Option<Bounds<Pixels>>,
         element_state: &mut InteractiveElementState,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> Style {
         let mut style = Style::default();
         style.refine(&self.base_style);
@@ -1921,7 +1926,9 @@ impl Interactivity {
                 let mouse_position = cx.mouse_position();
                 if !cx.has_active_drag() {
                     if let Some(group_hover) = self.group_hover_style.as_ref() {
-                        if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) {
+                        if let Some(group_bounds) =
+                            GroupBounds::get(&group_hover.group, cx.deref_mut())
+                        {
                             if group_bounds.contains(&mouse_position)
                                 && cx.was_top_layer(&mouse_position, cx.stacking_order())
                             {
@@ -1944,13 +1951,13 @@ impl Interactivity {
                 if let Some(drag) = cx.active_drag.take() {
                     let mut can_drop = true;
                     if let Some(can_drop_predicate) = &self.can_drop_predicate {
-                        can_drop = can_drop_predicate(drag.value.as_ref(), cx);
+                        can_drop = can_drop_predicate(drag.value.as_ref(), cx.deref_mut());
                     }
 
                     if can_drop {
                         for (state_type, group_drag_style) in &self.group_drag_over_styles {
                             if let Some(group_bounds) =
-                                GroupBounds::get(&group_drag_style.group, cx)
+                                GroupBounds::get(&group_drag_style.group, cx.deref_mut())
                             {
                                 if *state_type == drag.value.as_ref().type_id()
                                     && group_bounds.contains(&mouse_position)
@@ -2096,12 +2103,12 @@ where
     fn request_layout(
         &mut self,
         state: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         self.element.request_layout(state, cx)
     }
 
-    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) {
         self.element.paint(bounds, state, cx)
     }
 }
@@ -2171,12 +2178,12 @@ where
     fn request_layout(
         &mut self,
         state: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         self.element.request_layout(state, cx)
     }
 
-    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) {
         self.element.paint(bounds, state, cx)
     }
 }

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

@@ -1,9 +1,9 @@
 use std::sync::Arc;
 
 use crate::{
-    point, size, BorrowWindow, Bounds, DevicePixels, Element, ImageData, InteractiveElement,
+    point, size, Bounds, DevicePixels, Element, ElementContext, ImageData, InteractiveElement,
     InteractiveElementState, Interactivity, IntoElement, LayoutId, Pixels, SharedUrl, Size,
-    StyleRefinement, Styled, WindowContext,
+    StyleRefinement, Styled,
 };
 use futures::FutureExt;
 use media::core_video::CVImageBuffer;
@@ -81,7 +81,7 @@ impl Element for Img {
     fn request_layout(
         &mut self,
         element_state: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         self.interactivity
             .layout(element_state, cx, |style, cx| cx.request_layout(&style, []))
@@ -91,7 +91,7 @@ impl Element for Img {
         &mut self,
         bounds: Bounds<Pixels>,
         element_state: &mut Self::State,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let source = self.source.clone();
         self.interactivity.paint(

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

@@ -7,9 +7,9 @@
 //! If all of your elements are the same height, see [`UniformList`] for a simpler API
 
 use crate::{
-    point, px, AnyElement, AvailableSpace, BorrowAppContext, BorrowWindow, Bounds, ContentMask,
-    DispatchPhase, Element, IntoElement, Pixels, Point, ScrollWheelEvent, Size, Style,
-    StyleRefinement, Styled, WindowContext,
+    point, px, AnyElement, AvailableSpace, Bounds, ContentMask, DispatchPhase, Element,
+    IntoElement, Pixels, Point, ScrollWheelEvent, Size, Style, StyleRefinement, Styled,
+    WindowContext,
 };
 use collections::VecDeque;
 use refineable::Refineable as _;
@@ -359,7 +359,7 @@ impl Element for List {
     fn request_layout(
         &mut self,
         _state: Option<Self::State>,
-        cx: &mut crate::WindowContext,
+        cx: &mut crate::ElementContext,
     ) -> (crate::LayoutId, Self::State) {
         let mut style = Style::default();
         style.refine(&self.style);
@@ -373,7 +373,7 @@ impl Element for List {
         &mut self,
         bounds: Bounds<crate::Pixels>,
         _state: &mut Self::State,
-        cx: &mut crate::WindowContext,
+        cx: &mut crate::ElementContext,
     ) {
         let state = &mut *self.state.0.borrow_mut();
 

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

@@ -2,8 +2,8 @@ use smallvec::SmallVec;
 use taffy::style::{Display, Position};
 
 use crate::{
-    point, AnyElement, BorrowWindow, Bounds, Element, IntoElement, LayoutId, ParentElement, Pixels,
-    Point, Size, Style, WindowContext,
+    point, AnyElement, Bounds, Element, ElementContext, IntoElement, LayoutId, ParentElement,
+    Pixels, Point, Size, Style,
 };
 
 /// The state that the overlay element uses to track its children.
@@ -74,7 +74,7 @@ impl Element for Overlay {
     fn request_layout(
         &mut self,
         _: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (crate::LayoutId, Self::State) {
         let child_layout_ids = self
             .children
@@ -97,7 +97,7 @@ impl Element for Overlay {
         &mut self,
         bounds: crate::Bounds<crate::Pixels>,
         element_state: &mut Self::State,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         if element_state.child_layout_ids.is_empty() {
             return;

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

@@ -1,6 +1,6 @@
 use crate::{
-    Bounds, Element, ElementId, InteractiveElement, InteractiveElementState, Interactivity,
-    IntoElement, LayoutId, Pixels, SharedString, StyleRefinement, Styled, WindowContext,
+    Bounds, Element, ElementContext, ElementId, InteractiveElement, InteractiveElementState,
+    Interactivity, IntoElement, LayoutId, Pixels, SharedString, StyleRefinement, Styled,
 };
 use util::ResultExt;
 
@@ -32,7 +32,7 @@ impl Element for Svg {
     fn request_layout(
         &mut self,
         element_state: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         self.interactivity.layout(element_state, cx, |style, cx| {
             cx.request_layout(&style, None)
@@ -43,7 +43,7 @@ impl Element for Svg {
         &mut self,
         bounds: Bounds<Pixels>,
         element_state: &mut Self::State,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) where
         Self: Sized,
     {

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

@@ -1,7 +1,8 @@
 use crate::{
-    ActiveTooltip, AnyTooltip, AnyView, Bounds, DispatchPhase, Element, ElementId, HighlightStyle,
-    IntoElement, LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Point,
-    SharedString, Size, TextRun, TextStyle, WhiteSpace, WindowContext, WrappedLine, TOOLTIP_DELAY,
+    ActiveTooltip, AnyTooltip, AnyView, Bounds, DispatchPhase, Element, ElementContext, ElementId,
+    HighlightStyle, IntoElement, LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels,
+    Point, SharedString, Size, TextRun, TextStyle, WhiteSpace, WindowContext, WrappedLine,
+    TOOLTIP_DELAY,
 };
 use anyhow::anyhow;
 use parking_lot::{Mutex, MutexGuard};
@@ -21,14 +22,14 @@ impl Element for &'static str {
     fn request_layout(
         &mut self,
         _: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         let mut state = TextState::default();
         let layout_id = state.layout(SharedString::from(*self), None, cx);
         (layout_id, state)
     }
 
-    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut WindowContext) {
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut ElementContext) {
         state.paint(bounds, self, cx)
     }
 }
@@ -51,14 +52,14 @@ impl Element for SharedString {
     fn request_layout(
         &mut self,
         _: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         let mut state = TextState::default();
         let layout_id = state.layout(self.clone(), None, cx);
         (layout_id, state)
     }
 
-    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut WindowContext) {
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut ElementContext) {
         let text_str: &str = self.as_ref();
         state.paint(bounds, text_str, cx)
     }
@@ -130,14 +131,14 @@ impl Element for StyledText {
     fn request_layout(
         &mut self,
         _: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         let mut state = TextState::default();
         let layout_id = state.layout(self.text.clone(), self.runs.take(), cx);
         (layout_id, state)
     }
 
-    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) {
         state.paint(bounds, &self.text, cx)
     }
 }
@@ -174,7 +175,7 @@ impl TextState {
         &mut self,
         text: SharedString,
         runs: Option<Vec<TextRun>>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> LayoutId {
         let text_style = cx.text_style();
         let font_size = text_style.font_size.to_pixels(cx.rem_size());
@@ -249,7 +250,7 @@ impl TextState {
         layout_id
     }
 
-    fn paint(&mut self, bounds: Bounds<Pixels>, text: &str, cx: &mut WindowContext) {
+    fn paint(&mut self, bounds: Bounds<Pixels>, text: &str, cx: &mut ElementContext) {
         let element_state = self.lock();
         let element_state = element_state
             .as_ref()
@@ -377,7 +378,7 @@ impl Element for InteractiveText {
     fn request_layout(
         &mut self,
         state: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         if let Some(InteractiveTextState {
             mouse_down_index,
@@ -406,7 +407,7 @@ impl Element for InteractiveText {
         }
     }
 
-    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) {
         if let Some(click_listener) = self.click_listener.take() {
             let mouse_position = cx.mouse_position();
             if let Some(ix) = state.text_state.index_for_position(bounds, mouse_position) {

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

@@ -5,7 +5,7 @@
 //! elements with uniform height.
 
 use crate::{
-    point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Element,
+    point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, Element, ElementContext,
     ElementId, InteractiveElement, InteractiveElementState, Interactivity, IntoElement, LayoutId,
     Pixels, Render, Size, StyleRefinement, Styled, View, ViewContext, WindowContext,
 };
@@ -110,7 +110,7 @@ impl Element for UniformList {
     fn request_layout(
         &mut self,
         state: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         let max_items = self.item_count;
         let item_size = state
@@ -158,7 +158,7 @@ impl Element for UniformList {
         &mut self,
         bounds: Bounds<crate::Pixels>,
         element_state: &mut Self::State,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let style =
             self.interactivity
@@ -280,7 +280,7 @@ impl UniformList {
         self
     }
 
-    fn measure_item(&self, list_width: Option<Pixels>, cx: &mut WindowContext) -> Size<Pixels> {
+    fn measure_item(&self, list_width: Option<Pixels>, cx: &mut ElementContext) -> Size<Pixels> {
         if self.item_count == 0 {
             return Size::default();
         }

crates/gpui/src/gpui.rs 🔗

@@ -220,11 +220,6 @@ pub trait EventEmitter<E: Any>: 'static {}
 /// A helper trait for auto-implementing certain methods on contexts that
 /// can be used interchangeably.
 pub trait BorrowAppContext {
-    /// Run a closure with a text style pushed onto the context.
-    fn with_text_style<F, R>(&mut self, style: Option<TextStyleRefinement>, f: F) -> R
-    where
-        F: FnOnce(&mut Self) -> R;
-
     /// Set a global value on the context.
     fn set_global<T: 'static>(&mut self, global: T);
 }
@@ -233,20 +228,6 @@ impl<C> BorrowAppContext for C
 where
     C: BorrowMut<AppContext>,
 {
-    fn with_text_style<F, R>(&mut self, style: Option<TextStyleRefinement>, f: F) -> R
-    where
-        F: FnOnce(&mut Self) -> R,
-    {
-        if let Some(style) = style {
-            self.borrow_mut().push_text_style(style);
-            let result = f(self);
-            self.borrow_mut().pop_text_style();
-            result
-        } else {
-            f(self)
-        }
-    }
-
     fn set_global<G: 'static>(&mut self, global: G) {
         self.borrow_mut().set_global(global)
     }

crates/gpui/src/key_dispatch.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{
-    Action, ActionRegistry, DispatchPhase, EntityId, FocusId, KeyBinding, KeyContext, Keymap,
-    KeymatchResult, Keystroke, KeystrokeMatcher, WindowContext,
+    Action, ActionRegistry, DispatchPhase, ElementContext, EntityId, FocusId, KeyBinding,
+    KeyContext, Keymap, KeymatchResult, Keystroke, KeystrokeMatcher, WindowContext,
 };
 use collections::FxHashMap;
 use parking_lot::Mutex;
@@ -36,7 +36,7 @@ pub(crate) struct DispatchNode {
     parent: Option<DispatchNodeId>,
 }
 
-type KeyListener = Rc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>;
+type KeyListener = Rc<dyn Fn(&dyn Any, DispatchPhase, &mut ElementContext)>;
 
 #[derive(Clone)]
 pub(crate) struct DispatchActionListener {

crates/gpui/src/style.rs 🔗

@@ -1,10 +1,10 @@
 use std::{iter, mem, ops::Range};
 
 use crate::{
-    black, phi, point, quad, rems, AbsoluteLength, BorrowAppContext, BorrowWindow, Bounds,
-    ContentMask, Corners, CornersRefinement, CursorStyle, DefiniteLength, Edges, EdgesRefinement,
-    Font, FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rgba,
-    SharedString, Size, SizeRefinement, Styled, TextRun, WindowContext,
+    black, phi, point, quad, rems, AbsoluteLength, BorrowAppContext, Bounds, ContentMask, Corners,
+    CornersRefinement, CursorStyle, DefiniteLength, Edges, EdgesRefinement, ElementContext, Font,
+    FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rgba,
+    SharedString, Size, SizeRefinement, Styled, TextRun,
 };
 use collections::HashSet;
 use refineable::{Cascade, Refineable};
@@ -308,61 +308,12 @@ impl Style {
         }
     }
 
-    pub fn apply_text_style<C, F, R>(&self, cx: &mut C, f: F) -> R
-    where
-        C: BorrowAppContext,
-        F: FnOnce(&mut C) -> R,
-    {
-        if self.text.is_some() {
-            cx.with_text_style(Some(self.text.clone()), f)
-        } else {
-            f(cx)
-        }
-    }
-
-    /// Apply overflow to content mask
-    pub fn apply_overflow<C, F, R>(&self, bounds: Bounds<Pixels>, cx: &mut C, f: F) -> R
-    where
-        C: BorrowWindow,
-        F: FnOnce(&mut C) -> R,
-    {
-        let current_mask = cx.content_mask();
-
-        let min = current_mask.bounds.origin;
-        let max = current_mask.bounds.lower_right();
-
-        let mask_bounds = match (
-            self.overflow.x == Overflow::Visible,
-            self.overflow.y == Overflow::Visible,
-        ) {
-            // x and y both visible
-            (true, true) => return f(cx),
-            // x visible, y hidden
-            (true, false) => Bounds::from_corners(
-                point(min.x, bounds.origin.y),
-                point(max.x, bounds.lower_right().y),
-            ),
-            // x hidden, y visible
-            (false, true) => Bounds::from_corners(
-                point(bounds.origin.x, min.y),
-                point(bounds.lower_right().x, max.y),
-            ),
-            // both hidden
-            (false, false) => bounds,
-        };
-        let mask = ContentMask {
-            bounds: mask_bounds,
-        };
-
-        cx.with_content_mask(Some(mask), f)
-    }
-
     /// Paints the background of an element styled with this style.
     pub fn paint(
         &self,
         bounds: Bounds<Pixels>,
-        cx: &mut WindowContext,
-        continuation: impl FnOnce(&mut WindowContext),
+        cx: &mut ElementContext,
+        continuation: impl FnOnce(&mut ElementContext),
     ) {
         #[cfg(debug_assertions)]
         if self.debug_below {

crates/gpui/src/text_system/line.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{
-    black, fill, point, px, size, BorrowWindow, Bounds, Hsla, LineLayout, Pixels, Point, Result,
-    SharedString, UnderlineStyle, WindowContext, WrapBoundary, WrappedLineLayout,
+    black, fill, point, px, size, Bounds, ElementContext, Hsla, LineLayout, Pixels, Point, Result,
+    SharedString, UnderlineStyle, WrapBoundary, WrappedLineLayout,
 };
 use derive_more::{Deref, DerefMut};
 use smallvec::SmallVec;
@@ -33,7 +33,7 @@ impl ShapedLine {
         &self,
         origin: Point<Pixels>,
         line_height: Pixels,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> Result<()> {
         paint_line(
             origin,
@@ -66,7 +66,7 @@ impl WrappedLine {
         &self,
         origin: Point<Pixels>,
         line_height: Pixels,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> Result<()> {
         paint_line(
             origin,
@@ -87,7 +87,7 @@ fn paint_line(
     line_height: Pixels,
     decoration_runs: &[DecorationRun],
     wrap_boundaries: &[WrapBoundary],
-    cx: &mut WindowContext<'_>,
+    cx: &mut ElementContext<'_>,
 ) -> Result<()> {
     let padding_top = (line_height - layout.ascent - layout.descent) / 2.;
     let baseline_offset = point(px(0.), padding_top + layout.ascent);

crates/gpui/src/view.rs 🔗

@@ -1,8 +1,8 @@
 use crate::{
-    seal::Sealed, AnyElement, AnyModel, AnyWeakModel, AppContext, AvailableSpace, BorrowWindow,
-    Bounds, ContentMask, Element, ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView,
-    IntoElement, LayoutId, Model, Pixels, Point, Render, Size, StackingOrder, Style, TextStyle,
-    ViewContext, VisualContext, WeakModel, WindowContext,
+    seal::Sealed, AnyElement, AnyModel, AnyWeakModel, AppContext, AvailableSpace, Bounds,
+    ContentMask, Element, ElementContext, ElementId, Entity, EntityId, Flatten, FocusHandle,
+    FocusableView, IntoElement, LayoutId, Model, Pixels, Point, Render, Size, StackingOrder, Style,
+    TextStyle, ViewContext, VisualContext, WeakModel,
 };
 use anyhow::{Context, Result};
 use std::{
@@ -94,7 +94,7 @@ impl<V: Render> Element for View<V> {
     fn request_layout(
         &mut self,
         _state: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         cx.with_view_id(self.entity_id(), |cx| {
             let mut element = self.update(cx, |view, cx| view.render(cx).into_any_element());
@@ -103,7 +103,7 @@ impl<V: Render> Element for View<V> {
         })
     }
 
-    fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut WindowContext) {
+    fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut ElementContext) {
         cx.paint_view(self.entity_id(), |cx| element.take().unwrap().paint(cx));
     }
 }
@@ -202,7 +202,7 @@ impl<V> Eq for WeakView<V> {}
 #[derive(Clone, Debug)]
 pub struct AnyView {
     model: AnyModel,
-    request_layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, AnyElement),
+    request_layout: fn(&AnyView, &mut ElementContext) -> (LayoutId, AnyElement),
     cache: bool,
 }
 
@@ -250,7 +250,7 @@ impl AnyView {
         &self,
         origin: Point<Pixels>,
         available_space: Size<AvailableSpace>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         cx.paint_view(self.entity_id(), |cx| {
             cx.with_absolute_element_offset(origin, |cx| {
@@ -278,7 +278,7 @@ impl Element for AnyView {
     fn request_layout(
         &mut self,
         state: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         cx.with_view_id(self.entity_id(), |cx| {
             if self.cache {
@@ -299,7 +299,7 @@ impl Element for AnyView {
         })
     }
 
-    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) {
         cx.paint_view(self.entity_id(), |cx| {
             if !self.cache {
                 state.element.take().unwrap().paint(cx);
@@ -363,7 +363,7 @@ impl IntoElement for AnyView {
 /// A weak, dynamically-typed view handle that does not prevent the view from being released.
 pub struct AnyWeakView {
     model: AnyWeakModel,
-    layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, AnyElement),
+    layout: fn(&AnyView, &mut ElementContext) -> (LayoutId, AnyElement),
 }
 
 impl AnyWeakView {
@@ -402,11 +402,11 @@ impl std::fmt::Debug for AnyWeakView {
 }
 
 mod any_view {
-    use crate::{AnyElement, AnyView, IntoElement, LayoutId, Render, WindowContext};
+    use crate::{AnyElement, AnyView, ElementContext, IntoElement, LayoutId, Render};
 
     pub(crate) fn request_layout<V: 'static + Render>(
         view: &AnyView,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, AnyElement) {
         let view = view.clone().downcast::<V>().unwrap();
         let mut element = view.update(cx, |view, cx| view.render(cx).into_any_element());

crates/gpui/src/window.rs 🔗

@@ -1,31 +1,26 @@
 use crate::{
-    px, size, transparent_black, Action, AnyDrag, AnyTooltip, AnyView, AppContext, Arena,
-    AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle,
-    DevicePixels, DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect,
-    Entity, EntityId, EventEmitter, FileDropEvent, Flatten, FontId, GlobalElementId, GlyphId, Hsla,
-    ImageData, InputHandler, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeyEvent,
-    KeymatchResult, KeystrokeEvent, LayoutId, Model, ModelContext, Modifiers, MonochromeSprite,
-    MouseButton, MouseEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas,
-    PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point, PolychromeSprite,
-    PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels,
-    Scene, Shadow, SharedString, Size, Style, SubscriberSet, Subscription, Surface,
-    TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext, WeakView,
-    WindowBounds, WindowOptions, SUBPIXEL_VARIANTS,
+    px, size, transparent_black, Action, AnyDrag, AnyView, AppContext, Arena, AsyncWindowContext,
+    AvailableSpace, Bounds, Context, Corners, CursorStyle, DispatchActionListener, DispatchNodeId,
+    DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, Flatten,
+    GlobalElementId, Hsla, KeyBinding, KeyContext, KeyDownEvent, KeymatchResult, KeystrokeEvent,
+    Model, ModelContext, Modifiers, MouseButton, MouseMoveEvent, MouseUpEvent, Pixels,
+    PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptLevel, Render,
+    ScaledPixels, SharedString, Size, SubscriberSet, Subscription, TaffyLayoutEngine, Task, View,
+    VisualContext, WeakView, WindowBounds, WindowOptions,
 };
 use anyhow::{anyhow, Context as _, Result};
-use collections::{FxHashMap, FxHashSet};
+use collections::FxHashSet;
 use derive_more::{Deref, DerefMut};
 use futures::{
     channel::{mpsc, oneshot},
     StreamExt,
 };
-use media::core_video::CVImageBuffer;
 use parking_lot::RwLock;
 use slotmap::SlotMap;
 use smallvec::SmallVec;
 use std::{
     any::{Any, TypeId},
-    borrow::{Borrow, BorrowMut, Cow},
+    borrow::{Borrow, BorrowMut},
     cell::RefCell,
     collections::hash_map::Entry,
     fmt::{Debug, Display},
@@ -40,7 +35,10 @@ use std::{
     },
     time::Duration,
 };
-use util::{post_inc, ResultExt};
+use util::ResultExt;
+
+mod element_cx;
+pub use element_cx::*;
 
 const ACTIVE_DRAG_Z_INDEX: u8 = 1;
 
@@ -51,7 +49,7 @@ pub struct StackingOrder {
     #[deref]
     #[deref_mut]
     context_stack: SmallVec<[u8; 64]>,
-    id: u32,
+    pub(crate) id: u32,
 }
 
 impl std::fmt::Debug for StackingOrder {
@@ -99,7 +97,7 @@ impl DispatchPhase {
 }
 
 type AnyObserver = Box<dyn FnMut(&mut WindowContext) -> bool + 'static>;
-type AnyMouseListener = Box<dyn FnMut(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
+
 type AnyWindowFocusListener = Box<dyn FnMut(&FocusEvent, &mut WindowContext) -> bool + 'static>;
 
 struct FocusEvent {
@@ -259,8 +257,8 @@ pub struct Window {
     pub(crate) platform_window: Box<dyn PlatformWindow>,
     display_id: DisplayId,
     sprite_atlas: Arc<dyn PlatformAtlas>,
-    rem_size: Pixels,
-    viewport_size: Size<Pixels>,
+    pub(crate) rem_size: Pixels,
+    pub(crate) viewport_size: Size<Pixels>,
     layout_engine: Option<TaffyLayoutEngine>,
     pub(crate) root_view: Option<AnyView>,
     pub(crate) element_id_stack: GlobalElementId,
@@ -298,130 +296,10 @@ struct PendingInput {
 }
 
 pub(crate) struct ElementStateBox {
-    inner: Box<dyn Any>,
-    parent_view_id: EntityId,
+    pub(crate) inner: Box<dyn Any>,
+    pub(crate) parent_view_id: EntityId,
     #[cfg(debug_assertions)]
-    type_name: &'static str,
-}
-
-struct RequestedInputHandler {
-    view_id: EntityId,
-    handler: Option<PlatformInputHandler>,
-}
-
-struct TooltipRequest {
-    view_id: EntityId,
-    tooltip: AnyTooltip,
-}
-
-pub(crate) struct Frame {
-    focus: Option<FocusId>,
-    window_active: bool,
-    pub(crate) element_states: FxHashMap<GlobalElementId, ElementStateBox>,
-    mouse_listeners: FxHashMap<TypeId, Vec<(StackingOrder, EntityId, AnyMouseListener)>>,
-    pub(crate) dispatch_tree: DispatchTree,
-    pub(crate) scene: Scene,
-    pub(crate) depth_map: Vec<(StackingOrder, EntityId, Bounds<Pixels>)>,
-    pub(crate) z_index_stack: StackingOrder,
-    pub(crate) next_stacking_order_id: u32,
-    next_root_z_index: u8,
-    content_mask_stack: Vec<ContentMask<Pixels>>,
-    element_offset_stack: Vec<Point<Pixels>>,
-    requested_input_handler: Option<RequestedInputHandler>,
-    tooltip_request: Option<TooltipRequest>,
-    cursor_styles: FxHashMap<EntityId, CursorStyle>,
-    requested_cursor_style: Option<CursorStyle>,
-    pub(crate) view_stack: Vec<EntityId>,
-    pub(crate) reused_views: FxHashSet<EntityId>,
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub(crate) debug_bounds: collections::FxHashMap<String, Bounds<Pixels>>,
-}
-
-impl Frame {
-    fn new(dispatch_tree: DispatchTree) -> Self {
-        Frame {
-            focus: None,
-            window_active: false,
-            element_states: FxHashMap::default(),
-            mouse_listeners: FxHashMap::default(),
-            dispatch_tree,
-            scene: Scene::default(),
-            depth_map: Vec::new(),
-            z_index_stack: StackingOrder::default(),
-            next_stacking_order_id: 0,
-            next_root_z_index: 0,
-            content_mask_stack: Vec::new(),
-            element_offset_stack: Vec::new(),
-            requested_input_handler: None,
-            tooltip_request: None,
-            cursor_styles: FxHashMap::default(),
-            requested_cursor_style: None,
-            view_stack: Vec::new(),
-            reused_views: FxHashSet::default(),
-
-            #[cfg(any(test, feature = "test-support"))]
-            debug_bounds: FxHashMap::default(),
-        }
-    }
-
-    fn clear(&mut self) {
-        self.element_states.clear();
-        self.mouse_listeners.values_mut().for_each(Vec::clear);
-        self.dispatch_tree.clear();
-        self.depth_map.clear();
-        self.next_stacking_order_id = 0;
-        self.next_root_z_index = 0;
-        self.reused_views.clear();
-        self.scene.clear();
-        self.requested_input_handler.take();
-        self.tooltip_request.take();
-        self.cursor_styles.clear();
-        self.requested_cursor_style.take();
-        debug_assert_eq!(self.view_stack.len(), 0);
-    }
-
-    fn focus_path(&self) -> SmallVec<[FocusId; 8]> {
-        self.focus
-            .map(|focus_id| self.dispatch_tree.focus_path(focus_id))
-            .unwrap_or_default()
-    }
-
-    fn finish(&mut self, prev_frame: &mut Self) {
-        // Reuse mouse listeners that didn't change since the last frame.
-        for (type_id, listeners) in &mut prev_frame.mouse_listeners {
-            let next_listeners = self.mouse_listeners.entry(*type_id).or_default();
-            for (order, view_id, listener) in listeners.drain(..) {
-                if self.reused_views.contains(&view_id) {
-                    next_listeners.push((order, view_id, listener));
-                }
-            }
-        }
-
-        // Reuse entries in the depth map that didn't change since the last frame.
-        for (order, view_id, bounds) in prev_frame.depth_map.drain(..) {
-            if self.reused_views.contains(&view_id) {
-                match self
-                    .depth_map
-                    .binary_search_by(|(level, _, _)| order.cmp(level))
-                {
-                    Ok(i) | Err(i) => self.depth_map.insert(i, (order, view_id, bounds)),
-                }
-            }
-        }
-
-        // Retain element states for views that didn't change since the last frame.
-        for (element_id, state) in prev_frame.element_states.drain() {
-            if self.reused_views.contains(&state.parent_view_id) {
-                self.element_states.entry(element_id).or_insert(state);
-            }
-        }
-
-        // Reuse geometry that didn't change since the last frame.
-        self.scene
-            .reuse_views(&self.reused_views, &mut prev_frame.scene);
-        self.scene.finish();
-    }
+    pub(crate) type_name: &'static str,
 }
 
 impl Window {
@@ -815,80 +693,6 @@ impl<'a> WindowContext<'a> {
         result
     }
 
-    #[must_use]
-    /// Add a node to the layout tree for the current frame. Takes the `Style` of the element for which
-    /// layout is being requested, along with the layout ids of any children. This method is called during
-    /// calls to the `Element::layout` trait method and enables any element to participate in layout.
-    pub fn request_layout(
-        &mut self,
-        style: &Style,
-        children: impl IntoIterator<Item = LayoutId>,
-    ) -> LayoutId {
-        self.app.layout_id_buffer.clear();
-        self.app.layout_id_buffer.extend(children);
-        let rem_size = self.rem_size();
-
-        self.window.layout_engine.as_mut().unwrap().request_layout(
-            style,
-            rem_size,
-            &self.app.layout_id_buffer,
-        )
-    }
-
-    /// Add a node to the layout tree for the current frame. Instead of taking a `Style` and children,
-    /// this variant takes a function that is invoked during layout so you can use arbitrary logic to
-    /// determine the element's size. One place this is used internally is when measuring text.
-    ///
-    /// The given closure is invoked at layout time with the known dimensions and available space and
-    /// returns a `Size`.
-    pub fn request_measured_layout<
-        F: FnMut(Size<Option<Pixels>>, Size<AvailableSpace>, &mut WindowContext) -> Size<Pixels>
-            + 'static,
-    >(
-        &mut self,
-        style: Style,
-        measure: F,
-    ) -> LayoutId {
-        let rem_size = self.rem_size();
-        self.window
-            .layout_engine
-            .as_mut()
-            .unwrap()
-            .request_measured_layout(style, rem_size, measure)
-    }
-
-    pub(crate) fn layout_style(&self, layout_id: LayoutId) -> Option<&Style> {
-        self.window
-            .layout_engine
-            .as_ref()
-            .unwrap()
-            .requested_style(layout_id)
-    }
-
-    /// Compute the layout for the given id within the given available space.
-    /// This method is called for its side effect, typically by the framework prior to painting.
-    /// After calling it, you can request the bounds of the given layout node id or any descendant.
-    pub fn compute_layout(&mut self, layout_id: LayoutId, available_space: Size<AvailableSpace>) {
-        let mut layout_engine = self.window.layout_engine.take().unwrap();
-        layout_engine.compute_layout(layout_id, available_space, self);
-        self.window.layout_engine = Some(layout_engine);
-    }
-
-    /// Obtain the bounds computed for the given LayoutId relative to the window. This method should not
-    /// be invoked until the paint phase begins, and will usually be invoked by GPUI itself automatically
-    /// in order to pass your element its `Bounds` automatically.
-    pub fn layout_bounds(&mut self, layout_id: LayoutId) -> Bounds<Pixels> {
-        let mut bounds = self
-            .window
-            .layout_engine
-            .as_mut()
-            .unwrap()
-            .layout_bounds(layout_id)
-            .map(Into::into);
-        bounds.origin += self.element_offset();
-        bounds
-    }
-
     fn window_bounds_changed(&mut self) {
         self.window.scale_factor = self.window.platform_window.scale_factor();
         self.window.viewport_size = self.window.platform_window.content_size();
@@ -984,67 +788,6 @@ impl<'a> WindowContext<'a> {
         self.window.default_prevented
     }
 
-    /// Register a mouse event listener on the window for the next frame. The type of event
-    /// is determined by the first parameter of the given listener. When the next frame is rendered
-    /// the listener will be cleared.
-    pub fn on_mouse_event<Event: MouseEvent>(
-        &mut self,
-        mut handler: impl FnMut(&Event, DispatchPhase, &mut WindowContext) + 'static,
-    ) {
-        let view_id = self.parent_view_id();
-        let order = self.window.next_frame.z_index_stack.clone();
-        self.window
-            .next_frame
-            .mouse_listeners
-            .entry(TypeId::of::<Event>())
-            .or_default()
-            .push((
-                order,
-                view_id,
-                Box::new(
-                    move |event: &dyn Any, phase: DispatchPhase, cx: &mut WindowContext<'_>| {
-                        handler(event.downcast_ref().unwrap(), phase, cx)
-                    },
-                ),
-            ))
-    }
-
-    /// Register a key event listener on the window for the next frame. The type of event
-    /// is determined by the first parameter of the given listener. When the next frame is rendered
-    /// the listener will be cleared.
-    ///
-    /// This is a fairly low-level method, so prefer using event handlers on elements unless you have
-    /// a specific need to register a global listener.
-    pub fn on_key_event<Event: KeyEvent>(
-        &mut self,
-        listener: impl Fn(&Event, DispatchPhase, &mut WindowContext) + 'static,
-    ) {
-        self.window.next_frame.dispatch_tree.on_key_event(Rc::new(
-            move |event: &dyn Any, phase, cx: &mut WindowContext<'_>| {
-                if let Some(event) = event.downcast_ref::<Event>() {
-                    listener(event, phase, cx)
-                }
-            },
-        ));
-    }
-
-    /// Register an action listener on the window for the next frame. The type of action
-    /// is determined by the first parameter of the given listener. When the next frame is rendered
-    /// the listener will be cleared.
-    ///
-    /// This is a fairly low-level method, so prefer using action handlers on elements unless you have
-    /// a specific need to register a global listener.
-    pub fn on_action(
-        &mut self,
-        action_type: TypeId,
-        listener: impl Fn(&dyn Any, DispatchPhase, &mut WindowContext) + 'static,
-    ) {
-        self.window
-            .next_frame
-            .dispatch_tree
-            .on_action(action_type, Rc::new(listener));
-    }
-
     /// Determine whether the given action is available along the dispatch path to the currently focused element.
     pub fn is_action_available(&self, action: &dyn Action) -> bool {
         let target = self
@@ -1072,29 +815,6 @@ impl<'a> WindowContext<'a> {
         self.window.modifiers
     }
 
-    /// Updates the cursor style at the platform level.
-    pub fn set_cursor_style(&mut self, style: CursorStyle) {
-        let view_id = self.parent_view_id();
-        self.window.next_frame.cursor_styles.insert(view_id, style);
-        self.window.next_frame.requested_cursor_style = Some(style);
-    }
-
-    /// Sets a tooltip to be rendered for the upcoming frame
-    pub fn set_tooltip(&mut self, tooltip: AnyTooltip) {
-        let view_id = self.parent_view_id();
-        self.window.next_frame.tooltip_request = Some(TooltipRequest { view_id, tooltip });
-    }
-
-    /// Called during painting to track which z-index is on top at each pixel position
-    pub fn add_opaque_layer(&mut self, bounds: Bounds<Pixels>) {
-        let stacking_order = self.window.next_frame.z_index_stack.clone();
-        let view_id = self.parent_view_id();
-        let depth_map = &mut self.window.next_frame.depth_map;
-        match depth_map.binary_search_by(|(level, _, _)| stacking_order.cmp(level)) {
-            Ok(i) | Err(i) => depth_map.insert(i, (stacking_order, view_id, bounds)),
-        }
-    }
-
     /// Returns true if there is no opaque layer containing the given point
     /// on top of the given level. Layers whose level is an extension of the
     /// level are not considered to be on top of the level.
@@ -1136,371 +856,6 @@ impl<'a> WindowContext<'a> {
         &self.window.next_frame.z_index_stack
     }
 
-    /// Paint one or more drop shadows into the scene for the next frame at the current z-index.
-    pub fn paint_shadows(
-        &mut self,
-        bounds: Bounds<Pixels>,
-        corner_radii: Corners<Pixels>,
-        shadows: &[BoxShadow],
-    ) {
-        let scale_factor = self.scale_factor();
-        let content_mask = self.content_mask();
-        let view_id = self.parent_view_id();
-        let window = &mut *self.window;
-        for shadow in shadows {
-            let mut shadow_bounds = bounds;
-            shadow_bounds.origin += shadow.offset;
-            shadow_bounds.dilate(shadow.spread_radius);
-            window.next_frame.scene.insert(
-                &window.next_frame.z_index_stack,
-                Shadow {
-                    view_id: view_id.into(),
-                    layer_id: 0,
-                    order: 0,
-                    bounds: shadow_bounds.scale(scale_factor),
-                    content_mask: content_mask.scale(scale_factor),
-                    corner_radii: corner_radii.scale(scale_factor),
-                    color: shadow.color,
-                    blur_radius: shadow.blur_radius.scale(scale_factor),
-                },
-            );
-        }
-    }
-
-    /// Paint one or more quads into the scene for the next frame at the current stacking context.
-    /// Quads are colored rectangular regions with an optional background, border, and corner radius.
-    /// see [`fill`], [`outline`], and [`quad`] to construct this type.
-    pub fn paint_quad(&mut self, quad: PaintQuad) {
-        let scale_factor = self.scale_factor();
-        let content_mask = self.content_mask();
-        let view_id = self.parent_view_id();
-
-        let window = &mut *self.window;
-        window.next_frame.scene.insert(
-            &window.next_frame.z_index_stack,
-            Quad {
-                view_id: view_id.into(),
-                layer_id: 0,
-                order: 0,
-                bounds: quad.bounds.scale(scale_factor),
-                content_mask: content_mask.scale(scale_factor),
-                background: quad.background,
-                border_color: quad.border_color,
-                corner_radii: quad.corner_radii.scale(scale_factor),
-                border_widths: quad.border_widths.scale(scale_factor),
-            },
-        );
-    }
-
-    /// Paint the given `Path` into the scene for the next frame at the current z-index.
-    pub fn paint_path(&mut self, mut path: Path<Pixels>, color: impl Into<Hsla>) {
-        let scale_factor = self.scale_factor();
-        let content_mask = self.content_mask();
-        let view_id = self.parent_view_id();
-
-        path.content_mask = content_mask;
-        path.color = color.into();
-        path.view_id = view_id.into();
-        let window = &mut *self.window;
-        window
-            .next_frame
-            .scene
-            .insert(&window.next_frame.z_index_stack, path.scale(scale_factor));
-    }
-
-    /// Paint an underline into the scene for the next frame at the current z-index.
-    pub fn paint_underline(
-        &mut self,
-        origin: Point<Pixels>,
-        width: Pixels,
-        style: &UnderlineStyle,
-    ) {
-        let scale_factor = self.scale_factor();
-        let height = if style.wavy {
-            style.thickness * 3.
-        } else {
-            style.thickness
-        };
-        let bounds = Bounds {
-            origin,
-            size: size(width, height),
-        };
-        let content_mask = self.content_mask();
-        let view_id = self.parent_view_id();
-
-        let window = &mut *self.window;
-        window.next_frame.scene.insert(
-            &window.next_frame.z_index_stack,
-            Underline {
-                view_id: view_id.into(),
-                layer_id: 0,
-                order: 0,
-                bounds: bounds.scale(scale_factor),
-                content_mask: content_mask.scale(scale_factor),
-                thickness: style.thickness.scale(scale_factor),
-                color: style.color.unwrap_or_default(),
-                wavy: style.wavy,
-            },
-        );
-    }
-
-    /// Paint a monochrome (non-emoji) glyph into the scene for the next frame at the current z-index.
-    /// The y component of the origin is the baseline of the glyph.
-    pub fn paint_glyph(
-        &mut self,
-        origin: Point<Pixels>,
-        font_id: FontId,
-        glyph_id: GlyphId,
-        font_size: Pixels,
-        color: Hsla,
-    ) -> Result<()> {
-        let scale_factor = self.scale_factor();
-        let glyph_origin = origin.scale(scale_factor);
-        let subpixel_variant = Point {
-            x: (glyph_origin.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
-            y: (glyph_origin.y.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
-        };
-        let params = RenderGlyphParams {
-            font_id,
-            glyph_id,
-            font_size,
-            subpixel_variant,
-            scale_factor,
-            is_emoji: false,
-        };
-
-        let raster_bounds = self.text_system().raster_bounds(&params)?;
-        if !raster_bounds.is_zero() {
-            let tile =
-                self.window
-                    .sprite_atlas
-                    .get_or_insert_with(&params.clone().into(), &mut || {
-                        let (size, bytes) = self.text_system().rasterize_glyph(&params)?;
-                        Ok((size, Cow::Owned(bytes)))
-                    })?;
-            let bounds = Bounds {
-                origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into),
-                size: tile.bounds.size.map(Into::into),
-            };
-            let content_mask = self.content_mask().scale(scale_factor);
-            let view_id = self.parent_view_id();
-            let window = &mut *self.window;
-            window.next_frame.scene.insert(
-                &window.next_frame.z_index_stack,
-                MonochromeSprite {
-                    view_id: view_id.into(),
-                    layer_id: 0,
-                    order: 0,
-                    bounds,
-                    content_mask,
-                    color,
-                    tile,
-                },
-            );
-        }
-        Ok(())
-    }
-
-    /// Paint an emoji glyph into the scene for the next frame at the current z-index.
-    /// The y component of the origin is the baseline of the glyph.
-    pub fn paint_emoji(
-        &mut self,
-        origin: Point<Pixels>,
-        font_id: FontId,
-        glyph_id: GlyphId,
-        font_size: Pixels,
-    ) -> Result<()> {
-        let scale_factor = self.scale_factor();
-        let glyph_origin = origin.scale(scale_factor);
-        let params = RenderGlyphParams {
-            font_id,
-            glyph_id,
-            font_size,
-            // We don't render emojis with subpixel variants.
-            subpixel_variant: Default::default(),
-            scale_factor,
-            is_emoji: true,
-        };
-
-        let raster_bounds = self.text_system().raster_bounds(&params)?;
-        if !raster_bounds.is_zero() {
-            let tile =
-                self.window
-                    .sprite_atlas
-                    .get_or_insert_with(&params.clone().into(), &mut || {
-                        let (size, bytes) = self.text_system().rasterize_glyph(&params)?;
-                        Ok((size, Cow::Owned(bytes)))
-                    })?;
-            let bounds = Bounds {
-                origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into),
-                size: tile.bounds.size.map(Into::into),
-            };
-            let content_mask = self.content_mask().scale(scale_factor);
-            let view_id = self.parent_view_id();
-            let window = &mut *self.window;
-
-            window.next_frame.scene.insert(
-                &window.next_frame.z_index_stack,
-                PolychromeSprite {
-                    view_id: view_id.into(),
-                    layer_id: 0,
-                    order: 0,
-                    bounds,
-                    corner_radii: Default::default(),
-                    content_mask,
-                    tile,
-                    grayscale: false,
-                },
-            );
-        }
-        Ok(())
-    }
-
-    /// Paint a monochrome SVG into the scene for the next frame at the current stacking context.
-    pub fn paint_svg(
-        &mut self,
-        bounds: Bounds<Pixels>,
-        path: SharedString,
-        color: Hsla,
-    ) -> Result<()> {
-        let scale_factor = self.scale_factor();
-        let bounds = bounds.scale(scale_factor);
-        // Render the SVG at twice the size to get a higher quality result.
-        let params = RenderSvgParams {
-            path,
-            size: bounds
-                .size
-                .map(|pixels| DevicePixels::from((pixels.0 * 2.).ceil() as i32)),
-        };
-
-        let tile =
-            self.window
-                .sprite_atlas
-                .get_or_insert_with(&params.clone().into(), &mut || {
-                    let bytes = self.svg_renderer.render(&params)?;
-                    Ok((params.size, Cow::Owned(bytes)))
-                })?;
-        let content_mask = self.content_mask().scale(scale_factor);
-        let view_id = self.parent_view_id();
-
-        let window = &mut *self.window;
-        window.next_frame.scene.insert(
-            &window.next_frame.z_index_stack,
-            MonochromeSprite {
-                view_id: view_id.into(),
-                layer_id: 0,
-                order: 0,
-                bounds,
-                content_mask,
-                color,
-                tile,
-            },
-        );
-
-        Ok(())
-    }
-
-    /// Paint an image into the scene for the next frame at the current z-index.
-    pub fn paint_image(
-        &mut self,
-        bounds: Bounds<Pixels>,
-        corner_radii: Corners<Pixels>,
-        data: Arc<ImageData>,
-        grayscale: bool,
-    ) -> Result<()> {
-        let scale_factor = self.scale_factor();
-        let bounds = bounds.scale(scale_factor);
-        let params = RenderImageParams { image_id: data.id };
-
-        let tile = self
-            .window
-            .sprite_atlas
-            .get_or_insert_with(&params.clone().into(), &mut || {
-                Ok((data.size(), Cow::Borrowed(data.as_bytes())))
-            })?;
-        let content_mask = self.content_mask().scale(scale_factor);
-        let corner_radii = corner_radii.scale(scale_factor);
-        let view_id = self.parent_view_id();
-
-        let window = &mut *self.window;
-        window.next_frame.scene.insert(
-            &window.next_frame.z_index_stack,
-            PolychromeSprite {
-                view_id: view_id.into(),
-                layer_id: 0,
-                order: 0,
-                bounds,
-                content_mask,
-                corner_radii,
-                tile,
-                grayscale,
-            },
-        );
-        Ok(())
-    }
-
-    /// Paint a surface into the scene for the next frame at the current z-index.
-    pub fn paint_surface(&mut self, bounds: Bounds<Pixels>, image_buffer: CVImageBuffer) {
-        let scale_factor = self.scale_factor();
-        let bounds = bounds.scale(scale_factor);
-        let content_mask = self.content_mask().scale(scale_factor);
-        let view_id = self.parent_view_id();
-        let window = &mut *self.window;
-        window.next_frame.scene.insert(
-            &window.next_frame.z_index_stack,
-            Surface {
-                view_id: view_id.into(),
-                layer_id: 0,
-                order: 0,
-                bounds,
-                content_mask,
-                image_buffer,
-            },
-        );
-    }
-
-    pub(crate) fn reuse_view(&mut self) {
-        let view_id = self.parent_view_id();
-        let grafted_view_ids = self
-            .window
-            .next_frame
-            .dispatch_tree
-            .reuse_view(view_id, &mut self.window.rendered_frame.dispatch_tree);
-        for view_id in grafted_view_ids {
-            assert!(self.window.next_frame.reused_views.insert(view_id));
-
-            // Reuse the previous input handler requested during painting of the reused view.
-            if self
-                .window
-                .rendered_frame
-                .requested_input_handler
-                .as_ref()
-                .map_or(false, |requested| requested.view_id == view_id)
-            {
-                self.window.next_frame.requested_input_handler =
-                    self.window.rendered_frame.requested_input_handler.take();
-            }
-
-            // Reuse the tooltip previously requested during painting of the reused view.
-            if self
-                .window
-                .rendered_frame
-                .tooltip_request
-                .as_ref()
-                .map_or(false, |requested| requested.view_id == view_id)
-            {
-                self.window.next_frame.tooltip_request =
-                    self.window.rendered_frame.tooltip_request.take();
-            }
-
-            // Reuse the cursor styles previously requested during painting of the reused view.
-            if let Some(style) = self.window.rendered_frame.cursor_styles.remove(&view_id) {
-                self.window.next_frame.cursor_styles.insert(view_id, style);
-                self.window.next_frame.requested_cursor_style = Some(style);
-            }
-        }
-    }
-
     /// Draw pixels to the display for this window based on the contents of its scene.
     pub(crate) fn draw(&mut self) {
         self.window.dirty = false;
@@ -1513,44 +868,55 @@ impl<'a> WindowContext<'a> {
 
         if let Some(requested_handler) = self.window.rendered_frame.requested_input_handler.as_mut()
         {
-            requested_handler.handler = self.window.platform_window.take_input_handler();
+            let input_handler = self.window.platform_window.take_input_handler();
+            requested_handler.handler = input_handler;
         }
 
         let root_view = self.window.root_view.take().unwrap();
-
-        self.with_z_index(0, |cx| {
-            cx.with_key_dispatch(Some(KeyContext::default()), None, |_, cx| {
-                for (action_type, action_listeners) in &cx.app.global_action_listeners {
-                    for action_listener in action_listeners.iter().cloned() {
-                        cx.window.next_frame.dispatch_tree.on_action(
-                            *action_type,
-                            Rc::new(move |action: &dyn Any, phase, cx: &mut WindowContext<'_>| {
-                                action_listener(action, phase, cx)
-                            }),
-                        )
+        self.with_element_context(|cx| {
+            cx.with_z_index(0, |cx| {
+                cx.with_key_dispatch(Some(KeyContext::default()), None, |_, cx| {
+                    // We need to use cx.cx here so we can utilize borrow splitting
+                    for (action_type, action_listeners) in &cx.cx.app.global_action_listeners {
+                        for action_listener in action_listeners.iter().cloned() {
+                            cx.cx.window.next_frame.dispatch_tree.on_action(
+                                *action_type,
+                                Rc::new(
+                                    move |action: &dyn Any, phase, cx: &mut WindowContext<'_>| {
+                                        action_listener(action, phase, cx)
+                                    },
+                                ),
+                            )
+                        }
                     }
-                }
 
-                let available_space = cx.window.viewport_size.map(Into::into);
-                root_view.draw(Point::default(), available_space, cx);
+                    let available_space = cx.window.viewport_size.map(Into::into);
+                    root_view.draw(Point::default(), available_space, cx);
+                })
             })
         });
 
         if let Some(active_drag) = self.app.active_drag.take() {
-            self.with_z_index(ACTIVE_DRAG_Z_INDEX, |cx| {
-                let offset = cx.mouse_position() - active_drag.cursor_offset;
-                let available_space = size(AvailableSpace::MinContent, AvailableSpace::MinContent);
-                active_drag.view.draw(offset, available_space, cx);
+            self.with_element_context(|cx| {
+                cx.with_z_index(ACTIVE_DRAG_Z_INDEX, |cx| {
+                    let offset = cx.mouse_position() - active_drag.cursor_offset;
+                    let available_space =
+                        size(AvailableSpace::MinContent, AvailableSpace::MinContent);
+                    active_drag.view.draw(offset, available_space, cx);
+                })
             });
             self.active_drag = Some(active_drag);
         } else if let Some(tooltip_request) = self.window.next_frame.tooltip_request.take() {
-            self.with_z_index(1, |cx| {
-                let available_space = size(AvailableSpace::MinContent, AvailableSpace::MinContent);
-                tooltip_request.tooltip.view.draw(
-                    tooltip_request.tooltip.cursor_offset,
-                    available_space,
-                    cx,
-                );
+            self.with_element_context(|cx| {
+                cx.with_z_index(1, |cx| {
+                    let available_space =
+                        size(AvailableSpace::MinContent, AvailableSpace::MinContent);
+                    tooltip_request.tooltip.view.draw(
+                        tooltip_request.tooltip.cursor_offset,
+                        available_space,
+                        cx,
+                    );
+                })
             });
             self.window.next_frame.tooltip_request = Some(tooltip_request);
         }
@@ -1741,7 +1107,9 @@ impl<'a> WindowContext<'a> {
             // Capture phase, events bubble from back to front. Handlers for this phase are used for
             // special purposes, such as detecting events outside of a given Bounds.
             for (_, _, handler) in &mut handlers {
-                handler(event, DispatchPhase::Capture, self);
+                self.with_element_context(|cx| {
+                    handler(event, DispatchPhase::Capture, cx);
+                });
                 if !self.app.propagate_event {
                     break;
                 }
@@ -1750,7 +1118,9 @@ impl<'a> WindowContext<'a> {
             // Bubble phase, where most normal handlers do their work.
             if self.app.propagate_event {
                 for (_, _, handler) in handlers.iter_mut().rev() {
-                    handler(event, DispatchPhase::Bubble, self);
+                    self.with_element_context(|cx| {
+                        handler(event, DispatchPhase::Bubble, cx);
+                    });
                     if !self.app.propagate_event {
                         break;
                     }
@@ -1866,7 +1236,9 @@ impl<'a> WindowContext<'a> {
             let node = self.window.rendered_frame.dispatch_tree.node(*node_id);
 
             for key_listener in node.key_listeners.clone() {
-                key_listener(event, DispatchPhase::Capture, self);
+                self.with_element_context(|cx| {
+                    key_listener(event, DispatchPhase::Capture, cx);
+                });
                 if !self.propagate_event {
                     return;
                 }
@@ -1878,7 +1250,9 @@ impl<'a> WindowContext<'a> {
             // Handle low level key events
             let node = self.window.rendered_frame.dispatch_tree.node(*node_id);
             for key_listener in node.key_listeners.clone() {
-                key_listener(event, DispatchPhase::Bubble, self);
+                self.with_element_context(|cx| {
+                    key_listener(event, DispatchPhase::Bubble, cx);
+                });
                 if !self.propagate_event {
                     return;
                 }
@@ -1945,7 +1319,10 @@ impl<'a> WindowContext<'a> {
             {
                 let any_action = action.as_any();
                 if action_type == any_action.type_id() {
-                    listener(any_action, DispatchPhase::Capture, self);
+                    self.with_element_context(|cx| {
+                        listener(any_action, DispatchPhase::Capture, cx);
+                    });
+
                     if !self.propagate_event {
                         return;
                     }
@@ -1963,7 +1340,11 @@ impl<'a> WindowContext<'a> {
                 let any_action = action.as_any();
                 if action_type == any_action.type_id() {
                     self.propagate_event = false; // Actions stop propagation by default during the bubble phase
-                    listener(any_action, DispatchPhase::Bubble, self);
+
+                    self.with_element_context(|cx| {
+                        listener(any_action, DispatchPhase::Bubble, cx);
+                    });
+
                     if !self.propagate_event {
                         return;
                     }
@@ -2087,186 +1468,6 @@ impl<'a> WindowContext<'a> {
         }
     }
 
-    /// Invoke the given function with the given focus handle present on the key dispatch stack.
-    /// If you want an element to participate in key dispatch, use this method to push its key context and focus handle into the stack during paint.
-    pub fn with_key_dispatch<R>(
-        &mut self,
-        context: Option<KeyContext>,
-        focus_handle: Option<FocusHandle>,
-        f: impl FnOnce(Option<FocusHandle>, &mut Self) -> R,
-    ) -> R {
-        let window = &mut self.window;
-        let focus_id = focus_handle.as_ref().map(|handle| handle.id);
-        window
-            .next_frame
-            .dispatch_tree
-            .push_node(context.clone(), focus_id, None);
-
-        let result = f(focus_handle, self);
-
-        self.window.next_frame.dispatch_tree.pop_node();
-
-        result
-    }
-
-    /// Invoke the given function with the given view id present on the view stack.
-    /// This is a fairly low-level method used to layout views.
-    pub fn with_view_id<R>(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R {
-        let text_system = self.text_system().clone();
-        text_system.with_view(view_id, || {
-            if self.window.next_frame.view_stack.last() == Some(&view_id) {
-                return f(self);
-            } else {
-                self.window.next_frame.view_stack.push(view_id);
-                let result = f(self);
-                self.window.next_frame.view_stack.pop();
-                result
-            }
-        })
-    }
-
-    /// Invoke the given function with the given view id present on the view stack.
-    /// This is a fairly low-level method used to paint views.
-    pub fn paint_view<R>(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R {
-        let text_system = self.text_system().clone();
-        text_system.with_view(view_id, || {
-            if self.window.next_frame.view_stack.last() == Some(&view_id) {
-                return f(self);
-            } else {
-                self.window.next_frame.view_stack.push(view_id);
-                self.window
-                    .next_frame
-                    .dispatch_tree
-                    .push_node(None, None, Some(view_id));
-                let result = f(self);
-                self.window.next_frame.dispatch_tree.pop_node();
-                self.window.next_frame.view_stack.pop();
-                result
-            }
-        })
-    }
-
-    /// Updates or initializes state for an element with the given id that lives across multiple
-    /// frames. If an element with this ID existed in the rendered frame, its state will be passed
-    /// to the given closure. The state returned by the closure will be stored so it can be referenced
-    /// when drawing the next frame.
-    pub(crate) fn with_element_state<S, R>(
-        &mut self,
-        id: ElementId,
-        f: impl FnOnce(Option<S>, &mut Self) -> (R, S),
-    ) -> R
-    where
-        S: 'static,
-    {
-        self.with_element_id(Some(id), |cx| {
-            let global_id = cx.window().element_id_stack.clone();
-
-            if let Some(any) = cx
-                .window_mut()
-                .next_frame
-                .element_states
-                .remove(&global_id)
-                .or_else(|| {
-                    cx.window_mut()
-                        .rendered_frame
-                        .element_states
-                        .remove(&global_id)
-                })
-            {
-                let ElementStateBox {
-                    inner,
-                    parent_view_id,
-                    #[cfg(debug_assertions)]
-                    type_name
-                } = any;
-                // Using the extra inner option to avoid needing to reallocate a new box.
-                let mut state_box = inner
-                    .downcast::<Option<S>>()
-                    .map_err(|_| {
-                        #[cfg(debug_assertions)]
-                        {
-                            anyhow!(
-                                "invalid element state type for id, requested_type {:?}, actual type: {:?}",
-                                std::any::type_name::<S>(),
-                                type_name
-                            )
-                        }
-
-                        #[cfg(not(debug_assertions))]
-                        {
-                            anyhow!(
-                                "invalid element state type for id, requested_type {:?}",
-                                std::any::type_name::<S>(),
-                            )
-                        }
-                    })
-                    .unwrap();
-
-                // Actual: Option<AnyElement> <- View
-                // Requested: () <- AnyElement
-                let state = state_box
-                    .take()
-                    .expect("element state is already on the stack");
-                let (result, state) = f(Some(state), cx);
-                state_box.replace(state);
-                cx.window_mut()
-                    .next_frame
-                    .element_states
-                    .insert(global_id, ElementStateBox {
-                        inner: state_box,
-                        parent_view_id,
-                        #[cfg(debug_assertions)]
-                        type_name
-                    });
-                result
-            } else {
-                let (result, state) = f(None, cx);
-                let parent_view_id = cx.parent_view_id();
-                cx.window_mut()
-                    .next_frame
-                    .element_states
-                    .insert(global_id,
-                        ElementStateBox {
-                            inner: Box::new(Some(state)),
-                            parent_view_id,
-                            #[cfg(debug_assertions)]
-                            type_name: std::any::type_name::<S>()
-                        }
-
-                    );
-                result
-            }
-        })
-    }
-
-    fn parent_view_id(&self) -> EntityId {
-        *self
-            .window
-            .next_frame
-            .view_stack
-            .last()
-            .expect("a view should always be on the stack while drawing")
-    }
-
-    /// Sets an input handler, such as [`ElementInputHandler`][element_input_handler], which interfaces with the
-    /// platform to receive textual input with proper integration with concerns such
-    /// as IME interactions. This handler will be active for the upcoming frame until the following frame is
-    /// rendered.
-    ///
-    /// [element_input_handler]: crate::ElementInputHandler
-    pub fn handle_input(&mut self, focus_handle: &FocusHandle, input_handler: impl InputHandler) {
-        if focus_handle.is_focused(self) {
-            let view_id = self.parent_view_id();
-            self.window.next_frame.requested_input_handler = Some(RequestedInputHandler {
-                view_id,
-                handler: Some(PlatformInputHandler::new(
-                    self.to_async(),
-                    Box::new(input_handler),
-                )),
-            })
-        }
-    }
-
     /// Register a callback that can interrupt the closing of the current window based the returned boolean.
     /// If the callback returns false, the window won't be closed.
     pub fn on_window_should_close(&mut self, f: impl Fn(&mut WindowContext) -> bool + 'static) {

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

@@ -0,0 +1,1129 @@
+use std::{
+    any::{Any, TypeId},
+    borrow::{Borrow, BorrowMut, Cow},
+    mem,
+    rc::Rc,
+    sync::Arc,
+};
+
+use anyhow::Result;
+use collections::{FxHashMap, FxHashSet};
+use derive_more::{Deref, DerefMut};
+use media::core_video::CVImageBuffer;
+use smallvec::SmallVec;
+use util::post_inc;
+
+use crate::{
+    prelude::*, size, AnyTooltip, AppContext, AvailableSpace, Bounds, BoxShadow, ContentMask,
+    Corners, CursorStyle, DevicePixels, DispatchPhase, DispatchTree, ElementId, ElementStateBox,
+    EntityId, FocusHandle, FocusId, FontId, GlobalElementId, GlyphId, Hsla, ImageData,
+    InputHandler, IsZero, KeyContext, KeyEvent, LayoutId, MonochromeSprite, MouseEvent, PaintQuad,
+    Path, Pixels, PlatformInputHandler, Point, PolychromeSprite, Quad, RenderGlyphParams,
+    RenderImageParams, RenderSvgParams, Scene, Shadow, SharedString, Size, StackingOrder, Style,
+    Surface, TextStyleRefinement, Underline, UnderlineStyle, Window, WindowContext,
+    SUBPIXEL_VARIANTS,
+};
+
+type AnyMouseListener = Box<dyn FnMut(&dyn Any, DispatchPhase, &mut ElementContext) + 'static>;
+
+pub(crate) struct RequestedInputHandler {
+    pub(crate) view_id: EntityId,
+    pub(crate) handler: Option<PlatformInputHandler>,
+}
+
+pub(crate) struct TooltipRequest {
+    pub(crate) view_id: EntityId,
+    pub(crate) tooltip: AnyTooltip,
+}
+
+pub(crate) struct Frame {
+    pub(crate) focus: Option<FocusId>,
+    pub(crate) window_active: bool,
+    pub(crate) element_states: FxHashMap<GlobalElementId, ElementStateBox>,
+    pub(crate) mouse_listeners: FxHashMap<TypeId, Vec<(StackingOrder, EntityId, AnyMouseListener)>>,
+    pub(crate) dispatch_tree: DispatchTree,
+    pub(crate) scene: Scene,
+    pub(crate) depth_map: Vec<(StackingOrder, EntityId, Bounds<Pixels>)>,
+    pub(crate) z_index_stack: StackingOrder,
+    pub(crate) next_stacking_order_id: u32,
+    pub(crate) next_root_z_index: u8,
+    pub(crate) content_mask_stack: Vec<ContentMask<Pixels>>,
+    pub(crate) element_offset_stack: Vec<Point<Pixels>>,
+    pub(crate) requested_input_handler: Option<RequestedInputHandler>,
+    pub(crate) tooltip_request: Option<TooltipRequest>,
+    pub(crate) cursor_styles: FxHashMap<EntityId, CursorStyle>,
+    pub(crate) requested_cursor_style: Option<CursorStyle>,
+    pub(crate) view_stack: Vec<EntityId>,
+    pub(crate) reused_views: FxHashSet<EntityId>,
+
+    #[cfg(any(test, feature = "test-support"))]
+    pub(crate) debug_bounds: collections::FxHashMap<String, Bounds<Pixels>>,
+}
+
+impl Frame {
+    pub(crate) fn new(dispatch_tree: DispatchTree) -> Self {
+        Frame {
+            focus: None,
+            window_active: false,
+            element_states: FxHashMap::default(),
+            mouse_listeners: FxHashMap::default(),
+            dispatch_tree,
+            scene: Scene::default(),
+            depth_map: Vec::new(),
+            z_index_stack: StackingOrder::default(),
+            next_stacking_order_id: 0,
+            next_root_z_index: 0,
+            content_mask_stack: Vec::new(),
+            element_offset_stack: Vec::new(),
+            requested_input_handler: None,
+            tooltip_request: None,
+            cursor_styles: FxHashMap::default(),
+            requested_cursor_style: None,
+            view_stack: Vec::new(),
+            reused_views: FxHashSet::default(),
+
+            #[cfg(any(test, feature = "test-support"))]
+            debug_bounds: FxHashMap::default(),
+        }
+    }
+
+    pub(crate) fn clear(&mut self) {
+        self.element_states.clear();
+        self.mouse_listeners.values_mut().for_each(Vec::clear);
+        self.dispatch_tree.clear();
+        self.depth_map.clear();
+        self.next_stacking_order_id = 0;
+        self.next_root_z_index = 0;
+        self.reused_views.clear();
+        self.scene.clear();
+        self.requested_input_handler.take();
+        self.tooltip_request.take();
+        self.cursor_styles.clear();
+        self.requested_cursor_style.take();
+        debug_assert_eq!(self.view_stack.len(), 0);
+    }
+
+    pub(crate) fn focus_path(&self) -> SmallVec<[FocusId; 8]> {
+        self.focus
+            .map(|focus_id| self.dispatch_tree.focus_path(focus_id))
+            .unwrap_or_default()
+    }
+
+    pub(crate) fn finish(&mut self, prev_frame: &mut Self) {
+        // Reuse mouse listeners that didn't change since the last frame.
+        for (type_id, listeners) in &mut prev_frame.mouse_listeners {
+            let next_listeners = self.mouse_listeners.entry(*type_id).or_default();
+            for (order, view_id, listener) in listeners.drain(..) {
+                if self.reused_views.contains(&view_id) {
+                    next_listeners.push((order, view_id, listener));
+                }
+            }
+        }
+
+        // Reuse entries in the depth map that didn't change since the last frame.
+        for (order, view_id, bounds) in prev_frame.depth_map.drain(..) {
+            if self.reused_views.contains(&view_id) {
+                match self
+                    .depth_map
+                    .binary_search_by(|(level, _, _)| order.cmp(level))
+                {
+                    Ok(i) | Err(i) => self.depth_map.insert(i, (order, view_id, bounds)),
+                }
+            }
+        }
+
+        // Retain element states for views that didn't change since the last frame.
+        for (element_id, state) in prev_frame.element_states.drain() {
+            if self.reused_views.contains(&state.parent_view_id) {
+                self.element_states.entry(element_id).or_insert(state);
+            }
+        }
+
+        // Reuse geometry that didn't change since the last frame.
+        self.scene
+            .reuse_views(&self.reused_views, &mut prev_frame.scene);
+        self.scene.finish();
+    }
+}
+
+/// This context is used for assisting in the implementation of the element trait
+#[derive(Deref, DerefMut)]
+pub struct ElementContext<'a> {
+    pub(crate) cx: WindowContext<'a>,
+}
+
+impl<'a> WindowContext<'a> {
+    pub fn with_element_context<R>(&mut self, f: impl FnOnce(&mut ElementContext) -> R) -> R {
+        f(&mut ElementContext {
+            cx: WindowContext::new(self.app, self.window),
+        })
+    }
+}
+
+impl<'a> Borrow<AppContext> for ElementContext<'a> {
+    fn borrow(&self) -> &AppContext {
+        self.cx.app
+    }
+}
+
+impl<'a> BorrowMut<AppContext> for ElementContext<'a> {
+    fn borrow_mut(&mut self) -> &mut AppContext {
+        self.cx.borrow_mut()
+    }
+}
+
+impl<'a> Borrow<WindowContext<'a>> for ElementContext<'a> {
+    fn borrow(&self) -> &WindowContext<'a> {
+        &self.cx
+    }
+}
+
+impl<'a> BorrowMut<WindowContext<'a>> for ElementContext<'a> {
+    fn borrow_mut(&mut self) -> &mut WindowContext<'a> {
+        &mut self.cx
+    }
+}
+
+impl<'a> Borrow<Window> for ElementContext<'a> {
+    fn borrow(&self) -> &Window {
+        self.cx.window
+    }
+}
+
+impl<'a> BorrowMut<Window> for ElementContext<'a> {
+    fn borrow_mut(&mut self) -> &mut Window {
+        self.cx.borrow_mut()
+    }
+}
+
+impl<'a> Context for ElementContext<'a> {
+    type Result<T> = <WindowContext<'a> as Context>::Result<T>;
+
+    fn new_model<T: 'static>(
+        &mut self,
+        build_model: impl FnOnce(&mut crate::ModelContext<'_, T>) -> T,
+    ) -> Self::Result<crate::Model<T>> {
+        self.cx.new_model(build_model)
+    }
+
+    fn update_model<T, R>(
+        &mut self,
+        handle: &crate::Model<T>,
+        update: impl FnOnce(&mut T, &mut crate::ModelContext<'_, T>) -> R,
+    ) -> Self::Result<R>
+    where
+        T: 'static,
+    {
+        self.cx.update_model(handle, update)
+    }
+
+    fn read_model<T, R>(
+        &self,
+        handle: &crate::Model<T>,
+        read: impl FnOnce(&T, &AppContext) -> R,
+    ) -> Self::Result<R>
+    where
+        T: 'static,
+    {
+        self.cx.read_model(handle, read)
+    }
+
+    fn update_window<T, F>(&mut self, window: crate::AnyWindowHandle, f: F) -> Result<T>
+    where
+        F: FnOnce(crate::AnyView, &mut WindowContext<'_>) -> T,
+    {
+        self.cx.update_window(window, f)
+    }
+
+    fn read_window<T, R>(
+        &self,
+        window: &crate::WindowHandle<T>,
+        read: impl FnOnce(crate::View<T>, &AppContext) -> R,
+    ) -> Result<R>
+    where
+        T: 'static,
+    {
+        self.cx.read_window(window, read)
+    }
+}
+
+impl<'a> VisualContext for ElementContext<'a> {
+    fn new_view<V>(
+        &mut self,
+        build_view: impl FnOnce(&mut crate::ViewContext<'_, V>) -> V,
+    ) -> Self::Result<crate::View<V>>
+    where
+        V: 'static + Render,
+    {
+        self.cx.new_view(build_view)
+    }
+
+    fn update_view<V: 'static, R>(
+        &mut self,
+        view: &crate::View<V>,
+        update: impl FnOnce(&mut V, &mut crate::ViewContext<'_, V>) -> R,
+    ) -> Self::Result<R> {
+        self.cx.update_view(view, update)
+    }
+
+    fn replace_root_view<V>(
+        &mut self,
+        build_view: impl FnOnce(&mut crate::ViewContext<'_, V>) -> V,
+    ) -> Self::Result<crate::View<V>>
+    where
+        V: 'static + Render,
+    {
+        self.cx.replace_root_view(build_view)
+    }
+
+    fn focus_view<V>(&mut self, view: &crate::View<V>) -> Self::Result<()>
+    where
+        V: crate::FocusableView,
+    {
+        self.cx.focus_view(view)
+    }
+
+    fn dismiss_view<V>(&mut self, view: &crate::View<V>) -> Self::Result<()>
+    where
+        V: crate::ManagedView,
+    {
+        self.cx.dismiss_view(view)
+    }
+}
+
+impl<'a> ElementContext<'a> {
+    pub(crate) fn reuse_view(&mut self) {
+        let view_id = self.parent_view_id();
+        let grafted_view_ids = self
+            .cx
+            .window
+            .next_frame
+            .dispatch_tree
+            .reuse_view(view_id, &mut self.cx.window.rendered_frame.dispatch_tree);
+        for view_id in grafted_view_ids {
+            assert!(self.window.next_frame.reused_views.insert(view_id));
+
+            // Reuse the previous input handler requested during painting of the reused view.
+            if self
+                .window
+                .rendered_frame
+                .requested_input_handler
+                .as_ref()
+                .map_or(false, |requested| requested.view_id == view_id)
+            {
+                self.window.next_frame.requested_input_handler =
+                    self.window.rendered_frame.requested_input_handler.take();
+            }
+
+            // Reuse the tooltip previously requested during painting of the reused view.
+            if self
+                .window
+                .rendered_frame
+                .tooltip_request
+                .as_ref()
+                .map_or(false, |requested| requested.view_id == view_id)
+            {
+                self.window.next_frame.tooltip_request =
+                    self.window.rendered_frame.tooltip_request.take();
+            }
+
+            // Reuse the cursor styles previously requested during painting of the reused view.
+            if let Some(style) = self.window.rendered_frame.cursor_styles.remove(&view_id) {
+                self.window.next_frame.cursor_styles.insert(view_id, style);
+                self.window.next_frame.requested_cursor_style = Some(style);
+            }
+        }
+    }
+
+    pub fn with_text_style<F, R>(&mut self, style: Option<TextStyleRefinement>, f: F) -> R
+    where
+        F: FnOnce(&mut Self) -> R,
+    {
+        if let Some(style) = style {
+            self.push_text_style(style);
+            let result = f(self);
+            self.pop_text_style();
+            result
+        } else {
+            f(self)
+        }
+    }
+
+    /// Updates the cursor style at the platform level.
+    pub fn set_cursor_style(&mut self, style: CursorStyle) {
+        let view_id = self.parent_view_id();
+        self.window.next_frame.cursor_styles.insert(view_id, style);
+        self.window.next_frame.requested_cursor_style = Some(style);
+    }
+
+    /// Sets a tooltip to be rendered for the upcoming frame
+    pub fn set_tooltip(&mut self, tooltip: AnyTooltip) {
+        let view_id = self.parent_view_id();
+        self.window.next_frame.tooltip_request = Some(TooltipRequest { view_id, tooltip });
+    }
+
+    /// Pushes the given element id onto the global stack and invokes the given closure
+    /// with a `GlobalElementId`, which disambiguates the given id in the context of its ancestor
+    /// ids. Because elements are discarded and recreated on each frame, the `GlobalElementId` is
+    /// used to associate state with identified elements across separate frames.
+    pub fn with_element_id<R>(
+        &mut self,
+        id: Option<impl Into<ElementId>>,
+        f: impl FnOnce(&mut Self) -> R,
+    ) -> R {
+        if let Some(id) = id.map(Into::into) {
+            let window = self.window_mut();
+            window.element_id_stack.push(id);
+            let result = f(self);
+            let window: &mut Window = self.borrow_mut();
+            window.element_id_stack.pop();
+            result
+        } else {
+            f(self)
+        }
+    }
+
+    /// Invoke the given function with the given content mask after intersecting it
+    /// with the current mask.
+    pub fn with_content_mask<R>(
+        &mut self,
+        mask: Option<ContentMask<Pixels>>,
+        f: impl FnOnce(&mut Self) -> R,
+    ) -> R {
+        if let Some(mask) = mask {
+            let mask = mask.intersect(&self.content_mask());
+            self.window_mut().next_frame.content_mask_stack.push(mask);
+            let result = f(self);
+            self.window_mut().next_frame.content_mask_stack.pop();
+            result
+        } else {
+            f(self)
+        }
+    }
+
+    /// Invoke the given function with the content mask reset to that
+    /// of the window.
+    pub fn break_content_mask<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
+        let mask = ContentMask {
+            bounds: Bounds {
+                origin: Point::default(),
+                size: self.window().viewport_size,
+            },
+        };
+        let new_stacking_order_id =
+            post_inc(&mut self.window_mut().next_frame.next_stacking_order_id);
+        let new_root_z_index = post_inc(&mut self.window_mut().next_frame.next_root_z_index);
+        let old_stacking_order = mem::take(&mut self.window_mut().next_frame.z_index_stack);
+        self.window_mut().next_frame.z_index_stack.id = new_stacking_order_id;
+        self.window_mut()
+            .next_frame
+            .z_index_stack
+            .push(new_root_z_index);
+        self.window_mut().next_frame.content_mask_stack.push(mask);
+        let result = f(self);
+        self.window_mut().next_frame.content_mask_stack.pop();
+        self.window_mut().next_frame.z_index_stack = old_stacking_order;
+        result
+    }
+
+    /// Called during painting to invoke the given closure in a new stacking context. The given
+    /// z-index is interpreted relative to the previous call to `stack`.
+    pub fn with_z_index<R>(&mut self, z_index: u8, f: impl FnOnce(&mut Self) -> R) -> R {
+        let new_stacking_order_id =
+            post_inc(&mut self.window_mut().next_frame.next_stacking_order_id);
+        let old_stacking_order_id = mem::replace(
+            &mut self.window_mut().next_frame.z_index_stack.id,
+            new_stacking_order_id,
+        );
+        self.window_mut().next_frame.z_index_stack.id = new_stacking_order_id;
+        self.window_mut().next_frame.z_index_stack.push(z_index);
+        let result = f(self);
+        self.window_mut().next_frame.z_index_stack.id = old_stacking_order_id;
+        self.window_mut().next_frame.z_index_stack.pop();
+        result
+    }
+
+    /// Updates the global element offset relative to the current offset. This is used to implement
+    /// scrolling.
+    pub fn with_element_offset<R>(
+        &mut self,
+        offset: Point<Pixels>,
+        f: impl FnOnce(&mut Self) -> R,
+    ) -> R {
+        if offset.is_zero() {
+            return f(self);
+        };
+
+        let abs_offset = self.element_offset() + offset;
+        self.with_absolute_element_offset(abs_offset, f)
+    }
+
+    /// Updates the global element offset based on the given offset. This is used to implement
+    /// drag handles and other manual painting of elements.
+    pub fn with_absolute_element_offset<R>(
+        &mut self,
+        offset: Point<Pixels>,
+        f: impl FnOnce(&mut Self) -> R,
+    ) -> R {
+        self.window_mut()
+            .next_frame
+            .element_offset_stack
+            .push(offset);
+        let result = f(self);
+        self.window_mut().next_frame.element_offset_stack.pop();
+        result
+    }
+
+    /// Obtain the current element offset.
+    pub fn element_offset(&self) -> Point<Pixels> {
+        self.window()
+            .next_frame
+            .element_offset_stack
+            .last()
+            .copied()
+            .unwrap_or_default()
+    }
+
+    /// Obtain the current content mask.
+    pub fn content_mask(&self) -> ContentMask<Pixels> {
+        self.window()
+            .next_frame
+            .content_mask_stack
+            .last()
+            .cloned()
+            .unwrap_or_else(|| ContentMask {
+                bounds: Bounds {
+                    origin: Point::default(),
+                    size: self.window().viewport_size,
+                },
+            })
+    }
+
+    /// The size of an em for the base font of the application. Adjusting this value allows the
+    /// UI to scale, just like zooming a web page.
+    pub fn rem_size(&self) -> Pixels {
+        self.window().rem_size
+    }
+
+    /// Updates or initializes state for an element with the given id that lives across multiple
+    /// frames. If an element with this ID existed in the rendered frame, its state will be passed
+    /// to the given closure. The state returned by the closure will be stored so it can be referenced
+    /// when drawing the next frame.
+    pub fn with_element_state<S, R>(
+        &mut self,
+        id: ElementId,
+        f: impl FnOnce(Option<S>, &mut Self) -> (R, S),
+    ) -> R
+    where
+        S: 'static,
+    {
+        self.with_element_id(Some(id), |cx| {
+                let global_id = cx.window().element_id_stack.clone();
+
+                if let Some(any) = cx
+                    .window_mut()
+                    .next_frame
+                    .element_states
+                    .remove(&global_id)
+                    .or_else(|| {
+                        cx.window_mut()
+                            .rendered_frame
+                            .element_states
+                            .remove(&global_id)
+                    })
+                {
+                    let ElementStateBox {
+                        inner,
+                        parent_view_id,
+                        #[cfg(debug_assertions)]
+                        type_name
+                    } = any;
+                    // Using the extra inner option to avoid needing to reallocate a new box.
+                    let mut state_box = inner
+                        .downcast::<Option<S>>()
+                        .map_err(|_| {
+                            #[cfg(debug_assertions)]
+                            {
+                                anyhow::anyhow!(
+                                    "invalid element state type for id, requested_type {:?}, actual type: {:?}",
+                                    std::any::type_name::<S>(),
+                                    type_name
+                                )
+                            }
+
+                            #[cfg(not(debug_assertions))]
+                            {
+                                anyhow::anyhow!(
+                                    "invalid element state type for id, requested_type {:?}",
+                                    std::any::type_name::<S>(),
+                                )
+                            }
+                        })
+                        .unwrap();
+
+                    // Actual: Option<AnyElement> <- View
+                    // Requested: () <- AnyElement
+                    let state = state_box
+                        .take()
+                        .expect("element state is already on the stack");
+                    let (result, state) = f(Some(state), cx);
+                    state_box.replace(state);
+                    cx.window_mut()
+                        .next_frame
+                        .element_states
+                        .insert(global_id, ElementStateBox {
+                            inner: state_box,
+                            parent_view_id,
+                            #[cfg(debug_assertions)]
+                            type_name
+                        });
+                    result
+                } else {
+                    let (result, state) = f(None, cx);
+                    let parent_view_id = cx.parent_view_id();
+                    cx.window_mut()
+                        .next_frame
+                        .element_states
+                        .insert(global_id,
+                            ElementStateBox {
+                                inner: Box::new(Some(state)),
+                                parent_view_id,
+                                #[cfg(debug_assertions)]
+                                type_name: std::any::type_name::<S>()
+                            }
+
+                        );
+                    result
+                }
+            })
+    }
+    /// Paint one or more drop shadows into the scene for the next frame at the current z-index.
+    pub fn paint_shadows(
+        &mut self,
+        bounds: Bounds<Pixels>,
+        corner_radii: Corners<Pixels>,
+        shadows: &[BoxShadow],
+    ) {
+        let scale_factor = self.scale_factor();
+        let content_mask = self.content_mask();
+        let view_id = self.parent_view_id();
+        let window = &mut *self.window;
+        for shadow in shadows {
+            let mut shadow_bounds = bounds;
+            shadow_bounds.origin += shadow.offset;
+            shadow_bounds.dilate(shadow.spread_radius);
+            window.next_frame.scene.insert(
+                &window.next_frame.z_index_stack,
+                Shadow {
+                    view_id: view_id.into(),
+                    layer_id: 0,
+                    order: 0,
+                    bounds: shadow_bounds.scale(scale_factor),
+                    content_mask: content_mask.scale(scale_factor),
+                    corner_radii: corner_radii.scale(scale_factor),
+                    color: shadow.color,
+                    blur_radius: shadow.blur_radius.scale(scale_factor),
+                },
+            );
+        }
+    }
+
+    /// Paint one or more quads into the scene for the next frame at the current stacking context.
+    /// Quads are colored rectangular regions with an optional background, border, and corner radius.
+    /// see [`fill`], [`outline`], and [`quad`] to construct this type.
+    pub fn paint_quad(&mut self, quad: PaintQuad) {
+        let scale_factor = self.scale_factor();
+        let content_mask = self.content_mask();
+        let view_id = self.parent_view_id();
+
+        let window = &mut *self.window;
+        window.next_frame.scene.insert(
+            &window.next_frame.z_index_stack,
+            Quad {
+                view_id: view_id.into(),
+                layer_id: 0,
+                order: 0,
+                bounds: quad.bounds.scale(scale_factor),
+                content_mask: content_mask.scale(scale_factor),
+                background: quad.background,
+                border_color: quad.border_color,
+                corner_radii: quad.corner_radii.scale(scale_factor),
+                border_widths: quad.border_widths.scale(scale_factor),
+            },
+        );
+    }
+
+    /// Paint the given `Path` into the scene for the next frame at the current z-index.
+    pub fn paint_path(&mut self, mut path: Path<Pixels>, color: impl Into<Hsla>) {
+        let scale_factor = self.scale_factor();
+        let content_mask = self.content_mask();
+        let view_id = self.parent_view_id();
+
+        path.content_mask = content_mask;
+        path.color = color.into();
+        path.view_id = view_id.into();
+        let window = &mut *self.window;
+        window
+            .next_frame
+            .scene
+            .insert(&window.next_frame.z_index_stack, path.scale(scale_factor));
+    }
+
+    /// Paint an underline into the scene for the next frame at the current z-index.
+    pub fn paint_underline(
+        &mut self,
+        origin: Point<Pixels>,
+        width: Pixels,
+        style: &UnderlineStyle,
+    ) {
+        let scale_factor = self.scale_factor();
+        let height = if style.wavy {
+            style.thickness * 3.
+        } else {
+            style.thickness
+        };
+        let bounds = Bounds {
+            origin,
+            size: size(width, height),
+        };
+        let content_mask = self.content_mask();
+        let view_id = self.parent_view_id();
+
+        let window = &mut *self.window;
+        window.next_frame.scene.insert(
+            &window.next_frame.z_index_stack,
+            Underline {
+                view_id: view_id.into(),
+                layer_id: 0,
+                order: 0,
+                bounds: bounds.scale(scale_factor),
+                content_mask: content_mask.scale(scale_factor),
+                thickness: style.thickness.scale(scale_factor),
+                color: style.color.unwrap_or_default(),
+                wavy: style.wavy,
+            },
+        );
+    }
+
+    /// Paint a monochrome (non-emoji) glyph into the scene for the next frame at the current z-index.
+    /// The y component of the origin is the baseline of the glyph.
+    pub fn paint_glyph(
+        &mut self,
+        origin: Point<Pixels>,
+        font_id: FontId,
+        glyph_id: GlyphId,
+        font_size: Pixels,
+        color: Hsla,
+    ) -> Result<()> {
+        let scale_factor = self.scale_factor();
+        let glyph_origin = origin.scale(scale_factor);
+        let subpixel_variant = Point {
+            x: (glyph_origin.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
+            y: (glyph_origin.y.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
+        };
+        let params = RenderGlyphParams {
+            font_id,
+            glyph_id,
+            font_size,
+            subpixel_variant,
+            scale_factor,
+            is_emoji: false,
+        };
+
+        let raster_bounds = self.text_system().raster_bounds(&params)?;
+        if !raster_bounds.is_zero() {
+            let tile =
+                self.window
+                    .sprite_atlas
+                    .get_or_insert_with(&params.clone().into(), &mut || {
+                        let (size, bytes) = self.text_system().rasterize_glyph(&params)?;
+                        Ok((size, Cow::Owned(bytes)))
+                    })?;
+            let bounds = Bounds {
+                origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into),
+                size: tile.bounds.size.map(Into::into),
+            };
+            let content_mask = self.content_mask().scale(scale_factor);
+            let view_id = self.parent_view_id();
+            let window = &mut *self.window;
+            window.next_frame.scene.insert(
+                &window.next_frame.z_index_stack,
+                MonochromeSprite {
+                    view_id: view_id.into(),
+                    layer_id: 0,
+                    order: 0,
+                    bounds,
+                    content_mask,
+                    color,
+                    tile,
+                },
+            );
+        }
+        Ok(())
+    }
+
+    /// Paint an emoji glyph into the scene for the next frame at the current z-index.
+    /// The y component of the origin is the baseline of the glyph.
+    pub fn paint_emoji(
+        &mut self,
+        origin: Point<Pixels>,
+        font_id: FontId,
+        glyph_id: GlyphId,
+        font_size: Pixels,
+    ) -> Result<()> {
+        let scale_factor = self.scale_factor();
+        let glyph_origin = origin.scale(scale_factor);
+        let params = RenderGlyphParams {
+            font_id,
+            glyph_id,
+            font_size,
+            // We don't render emojis with subpixel variants.
+            subpixel_variant: Default::default(),
+            scale_factor,
+            is_emoji: true,
+        };
+
+        let raster_bounds = self.text_system().raster_bounds(&params)?;
+        if !raster_bounds.is_zero() {
+            let tile =
+                self.window
+                    .sprite_atlas
+                    .get_or_insert_with(&params.clone().into(), &mut || {
+                        let (size, bytes) = self.text_system().rasterize_glyph(&params)?;
+                        Ok((size, Cow::Owned(bytes)))
+                    })?;
+            let bounds = Bounds {
+                origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into),
+                size: tile.bounds.size.map(Into::into),
+            };
+            let content_mask = self.content_mask().scale(scale_factor);
+            let view_id = self.parent_view_id();
+            let window = &mut *self.window;
+
+            window.next_frame.scene.insert(
+                &window.next_frame.z_index_stack,
+                PolychromeSprite {
+                    view_id: view_id.into(),
+                    layer_id: 0,
+                    order: 0,
+                    bounds,
+                    corner_radii: Default::default(),
+                    content_mask,
+                    tile,
+                    grayscale: false,
+                },
+            );
+        }
+        Ok(())
+    }
+
+    /// Paint a monochrome SVG into the scene for the next frame at the current stacking context.
+    pub fn paint_svg(
+        &mut self,
+        bounds: Bounds<Pixels>,
+        path: SharedString,
+        color: Hsla,
+    ) -> Result<()> {
+        let scale_factor = self.scale_factor();
+        let bounds = bounds.scale(scale_factor);
+        // Render the SVG at twice the size to get a higher quality result.
+        let params = RenderSvgParams {
+            path,
+            size: bounds
+                .size
+                .map(|pixels| DevicePixels::from((pixels.0 * 2.).ceil() as i32)),
+        };
+
+        let tile =
+            self.window
+                .sprite_atlas
+                .get_or_insert_with(&params.clone().into(), &mut || {
+                    let bytes = self.svg_renderer.render(&params)?;
+                    Ok((params.size, Cow::Owned(bytes)))
+                })?;
+        let content_mask = self.content_mask().scale(scale_factor);
+        let view_id = self.parent_view_id();
+
+        let window = &mut *self.window;
+        window.next_frame.scene.insert(
+            &window.next_frame.z_index_stack,
+            MonochromeSprite {
+                view_id: view_id.into(),
+                layer_id: 0,
+                order: 0,
+                bounds,
+                content_mask,
+                color,
+                tile,
+            },
+        );
+
+        Ok(())
+    }
+
+    /// Paint an image into the scene for the next frame at the current z-index.
+    pub fn paint_image(
+        &mut self,
+        bounds: Bounds<Pixels>,
+        corner_radii: Corners<Pixels>,
+        data: Arc<ImageData>,
+        grayscale: bool,
+    ) -> Result<()> {
+        let scale_factor = self.scale_factor();
+        let bounds = bounds.scale(scale_factor);
+        let params = RenderImageParams { image_id: data.id };
+
+        let tile = self
+            .window
+            .sprite_atlas
+            .get_or_insert_with(&params.clone().into(), &mut || {
+                Ok((data.size(), Cow::Borrowed(data.as_bytes())))
+            })?;
+        let content_mask = self.content_mask().scale(scale_factor);
+        let corner_radii = corner_radii.scale(scale_factor);
+        let view_id = self.parent_view_id();
+
+        let window = &mut *self.window;
+        window.next_frame.scene.insert(
+            &window.next_frame.z_index_stack,
+            PolychromeSprite {
+                view_id: view_id.into(),
+                layer_id: 0,
+                order: 0,
+                bounds,
+                content_mask,
+                corner_radii,
+                tile,
+                grayscale,
+            },
+        );
+        Ok(())
+    }
+
+    /// Paint a surface into the scene for the next frame at the current z-index.
+    pub fn paint_surface(&mut self, bounds: Bounds<Pixels>, image_buffer: CVImageBuffer) {
+        let scale_factor = self.scale_factor();
+        let bounds = bounds.scale(scale_factor);
+        let content_mask = self.content_mask().scale(scale_factor);
+        let view_id = self.parent_view_id();
+        let window = &mut *self.window;
+        window.next_frame.scene.insert(
+            &window.next_frame.z_index_stack,
+            Surface {
+                view_id: view_id.into(),
+                layer_id: 0,
+                order: 0,
+                bounds,
+                content_mask,
+                image_buffer,
+            },
+        );
+    }
+
+    #[must_use]
+    /// Add a node to the layout tree for the current frame. Takes the `Style` of the element for which
+    /// layout is being requested, along with the layout ids of any children. This method is called during
+    /// calls to the `Element::layout` trait method and enables any element to participate in layout.
+    pub fn request_layout(
+        &mut self,
+        style: &Style,
+        children: impl IntoIterator<Item = LayoutId>,
+    ) -> LayoutId {
+        self.app.layout_id_buffer.clear();
+        self.app.layout_id_buffer.extend(children);
+        let rem_size = self.rem_size();
+
+        self.cx
+            .window
+            .layout_engine
+            .as_mut()
+            .unwrap()
+            .request_layout(style, rem_size, &self.cx.app.layout_id_buffer)
+    }
+
+    /// Add a node to the layout tree for the current frame. Instead of taking a `Style` and children,
+    /// this variant takes a function that is invoked during layout so you can use arbitrary logic to
+    /// determine the element's size. One place this is used internally is when measuring text.
+    ///
+    /// The given closure is invoked at layout time with the known dimensions and available space and
+    /// returns a `Size`.
+    pub fn request_measured_layout<
+        F: FnMut(Size<Option<Pixels>>, Size<AvailableSpace>, &mut WindowContext) -> Size<Pixels>
+            + 'static,
+    >(
+        &mut self,
+        style: Style,
+        measure: F,
+    ) -> LayoutId {
+        let rem_size = self.rem_size();
+        self.window
+            .layout_engine
+            .as_mut()
+            .unwrap()
+            .request_measured_layout(style, rem_size, measure)
+    }
+
+    /// Compute the layout for the given id within the given available space.
+    /// This method is called for its side effect, typically by the framework prior to painting.
+    /// After calling it, you can request the bounds of the given layout node id or any descendant.
+    pub fn compute_layout(&mut self, layout_id: LayoutId, available_space: Size<AvailableSpace>) {
+        let mut layout_engine = self.window.layout_engine.take().unwrap();
+        layout_engine.compute_layout(layout_id, available_space, self);
+        self.window.layout_engine = Some(layout_engine);
+    }
+
+    /// Obtain the bounds computed for the given LayoutId relative to the window. This method should not
+    /// be invoked until the paint phase begins, and will usually be invoked by GPUI itself automatically
+    /// in order to pass your element its `Bounds` automatically.
+    pub fn layout_bounds(&mut self, layout_id: LayoutId) -> Bounds<Pixels> {
+        let mut bounds = self
+            .window
+            .layout_engine
+            .as_mut()
+            .unwrap()
+            .layout_bounds(layout_id)
+            .map(Into::into);
+        bounds.origin += self.element_offset();
+        bounds
+    }
+
+    pub(crate) fn layout_style(&self, layout_id: LayoutId) -> Option<&Style> {
+        self.window
+            .layout_engine
+            .as_ref()
+            .unwrap()
+            .requested_style(layout_id)
+    }
+
+    /// Called during painting to track which z-index is on top at each pixel position
+    pub fn add_opaque_layer(&mut self, bounds: Bounds<Pixels>) {
+        let stacking_order = self.window.next_frame.z_index_stack.clone();
+        let view_id = self.parent_view_id();
+        let depth_map = &mut self.window.next_frame.depth_map;
+        match depth_map.binary_search_by(|(level, _, _)| stacking_order.cmp(level)) {
+            Ok(i) | Err(i) => depth_map.insert(i, (stacking_order, view_id, bounds)),
+        }
+    }
+
+    /// Invoke the given function with the given focus handle present on the key dispatch stack.
+    /// If you want an element to participate in key dispatch, use this method to push its key context and focus handle into the stack during paint.
+    pub fn with_key_dispatch<R>(
+        &mut self,
+        context: Option<KeyContext>,
+        focus_handle: Option<FocusHandle>,
+        f: impl FnOnce(Option<FocusHandle>, &mut Self) -> R,
+    ) -> R {
+        let window = &mut self.window;
+        let focus_id = focus_handle.as_ref().map(|handle| handle.id);
+        window
+            .next_frame
+            .dispatch_tree
+            .push_node(context.clone(), focus_id, None);
+
+        let result = f(focus_handle, self);
+
+        self.window.next_frame.dispatch_tree.pop_node();
+
+        result
+    }
+
+    /// Invoke the given function with the given view id present on the view stack.
+    /// This is a fairly low-level method used to layout views.
+    pub fn with_view_id<R>(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R {
+        let text_system = self.text_system().clone();
+        text_system.with_view(view_id, || {
+            if self.window.next_frame.view_stack.last() == Some(&view_id) {
+                return f(self);
+            } else {
+                self.window.next_frame.view_stack.push(view_id);
+                let result = f(self);
+                self.window.next_frame.view_stack.pop();
+                result
+            }
+        })
+    }
+
+    /// Invoke the given function with the given view id present on the view stack.
+    /// This is a fairly low-level method used to paint views.
+    pub fn paint_view<R>(&mut self, view_id: EntityId, f: impl FnOnce(&mut Self) -> R) -> R {
+        let text_system = self.text_system().clone();
+        text_system.with_view(view_id, || {
+            if self.window.next_frame.view_stack.last() == Some(&view_id) {
+                return f(self);
+            } else {
+                self.window.next_frame.view_stack.push(view_id);
+                self.window
+                    .next_frame
+                    .dispatch_tree
+                    .push_node(None, None, Some(view_id));
+                let result = f(self);
+                self.window.next_frame.dispatch_tree.pop_node();
+                self.window.next_frame.view_stack.pop();
+                result
+            }
+        })
+    }
+
+    /// Sets an input handler, such as [`ElementInputHandler`][element_input_handler], which interfaces with the
+    /// platform to receive textual input with proper integration with concerns such
+    /// as IME interactions. This handler will be active for the upcoming frame until the following frame is
+    /// rendered.
+    ///
+    /// [element_input_handler]: crate::ElementInputHandler
+    pub fn handle_input(&mut self, focus_handle: &FocusHandle, input_handler: impl InputHandler) {
+        if focus_handle.is_focused(self) {
+            let view_id = self.parent_view_id();
+            self.window.next_frame.requested_input_handler = Some(RequestedInputHandler {
+                view_id,
+                handler: Some(PlatformInputHandler::new(
+                    self.to_async(),
+                    Box::new(input_handler),
+                )),
+            })
+        }
+    }
+
+    /// Register a mouse event listener on the window for the next frame. The type of event
+    /// is determined by the first parameter of the given listener. When the next frame is rendered
+    /// the listener will be cleared.
+    pub fn on_mouse_event<Event: MouseEvent>(
+        &mut self,
+        mut handler: impl FnMut(&Event, DispatchPhase, &mut ElementContext) + 'static,
+    ) {
+        let view_id = self.parent_view_id();
+        let order = self.window.next_frame.z_index_stack.clone();
+        self.window
+            .next_frame
+            .mouse_listeners
+            .entry(TypeId::of::<Event>())
+            .or_default()
+            .push((
+                order,
+                view_id,
+                Box::new(
+                    move |event: &dyn Any, phase: DispatchPhase, cx: &mut ElementContext<'_>| {
+                        handler(event.downcast_ref().unwrap(), phase, cx)
+                    },
+                ),
+            ))
+    }
+
+    /// Register a key event listener on the window for the next frame. The type of event
+    /// is determined by the first parameter of the given listener. When the next frame is rendered
+    /// the listener will be cleared.
+    ///
+    /// This is a fairly low-level method, so prefer using event handlers on elements unless you have
+    /// a specific need to register a global listener.
+    pub fn on_key_event<Event: KeyEvent>(
+        &mut self,
+        listener: impl Fn(&Event, DispatchPhase, &mut ElementContext) + 'static,
+    ) {
+        self.window.next_frame.dispatch_tree.on_key_event(Rc::new(
+            move |event: &dyn Any, phase, cx: &mut ElementContext<'_>| {
+                if let Some(event) = event.downcast_ref::<Event>() {
+                    listener(event, phase, cx)
+                }
+            },
+        ));
+    }
+}

crates/terminal_view/src/terminal_element.rs 🔗

@@ -1,11 +1,11 @@
 use editor::{Cursor, HighlightedRange, HighlightedRangeLine};
 use gpui::{
-    div, fill, point, px, relative, AnyElement, AvailableSpace, BorrowWindow, Bounds,
-    DispatchPhase, Element, ElementId, FocusHandle, Font, FontStyle, FontWeight, HighlightStyle,
-    Hsla, InputHandler, InteractiveBounds, InteractiveElement, InteractiveElementState,
-    Interactivity, IntoElement, LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton,
-    MouseMoveEvent, Pixels, Point, ShapedLine, StatefulInteractiveElement, Styled, TextRun,
-    TextStyle, TextSystem, UnderlineStyle, WeakView, WhiteSpace, WindowContext,
+    div, fill, point, px, relative, AnyElement, AvailableSpace, Bounds, DispatchPhase, Element,
+    ElementContext, ElementId, FocusHandle, Font, FontStyle, FontWeight, HighlightStyle, Hsla,
+    InputHandler, InteractiveBounds, InteractiveElement, InteractiveElementState, Interactivity,
+    IntoElement, LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton, MouseMoveEvent,
+    Pixels, Point, ShapedLine, StatefulInteractiveElement, Styled, TextRun, TextStyle, TextSystem,
+    UnderlineStyle, WeakView, WhiteSpace, WindowContext,
 };
 use itertools::Itertools;
 use language::CursorShape;
@@ -81,7 +81,7 @@ impl LayoutCell {
         origin: Point<Pixels>,
         layout: &LayoutState,
         _visible_bounds: Bounds<Pixels>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let pos = {
             let point = self.point;
@@ -120,7 +120,7 @@ impl LayoutRect {
         }
     }
 
-    fn paint(&self, origin: Point<Pixels>, layout: &LayoutState, cx: &mut WindowContext) {
+    fn paint(&self, origin: Point<Pixels>, layout: &LayoutState, cx: &mut ElementContext) {
         let position = {
             let alac_point = self.point;
             point(
@@ -365,7 +365,7 @@ impl TerminalElement {
         result
     }
 
-    fn compute_layout(&self, bounds: Bounds<gpui::Pixels>, cx: &mut WindowContext) -> LayoutState {
+    fn compute_layout(&self, bounds: Bounds<gpui::Pixels>, cx: &mut ElementContext) -> LayoutState {
         let settings = ThemeSettings::get_global(cx).clone();
 
         let buffer_font_size = settings.buffer_font_size(cx);
@@ -590,7 +590,7 @@ impl TerminalElement {
         origin: Point<Pixels>,
         mode: TermMode,
         bounds: Bounds<Pixels>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         let focus = self.focus.clone();
         let terminal = self.terminal.clone();
@@ -722,7 +722,7 @@ impl Element for TerminalElement {
     fn request_layout(
         &mut self,
         element_state: Option<Self::State>,
-        cx: &mut WindowContext<'_>,
+        cx: &mut ElementContext<'_>,
     ) -> (LayoutId, Self::State) {
         let (layout_id, interactive_state) =
             self.interactivity
@@ -741,7 +741,7 @@ impl Element for TerminalElement {
         &mut self,
         bounds: Bounds<Pixels>,
         state: &mut Self::State,
-        cx: &mut WindowContext<'_>,
+        cx: &mut ElementContext<'_>,
     ) {
         let mut layout = self.compute_layout(bounds, cx);
 

crates/ui/src/components/popover_menu.rs 🔗

@@ -2,8 +2,9 @@ use std::{cell::RefCell, rc::Rc};
 
 use gpui::{
     overlay, point, prelude::FluentBuilder, px, rems, AnchorCorner, AnyElement, Bounds,
-    DismissEvent, DispatchPhase, Element, ElementId, InteractiveBounds, IntoElement, LayoutId,
-    ManagedView, MouseDownEvent, ParentElement, Pixels, Point, View, VisualContext, WindowContext,
+    DismissEvent, DispatchPhase, Element, ElementContext, ElementId, InteractiveBounds,
+    IntoElement, LayoutId, ManagedView, MouseDownEvent, ParentElement, Pixels, Point, View,
+    VisualContext, WindowContext,
 };
 
 use crate::{Clickable, Selectable};
@@ -134,7 +135,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
     fn request_layout(
         &mut self,
         element_state: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (gpui::LayoutId, Self::State) {
         let mut menu_layout_id = None;
 
@@ -188,7 +189,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
         &mut self,
         _: Bounds<gpui::Pixels>,
         element_state: &mut Self::State,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         if let Some(mut child) = element_state.child_element.take() {
             child.paint(cx);

crates/ui/src/components/right_click_menu.rs 🔗

@@ -1,9 +1,9 @@
 use std::{cell::RefCell, rc::Rc};
 
 use gpui::{
-    overlay, AnchorCorner, AnyElement, BorrowWindow, Bounds, DismissEvent, DispatchPhase, Element,
-    ElementId, InteractiveBounds, IntoElement, LayoutId, ManagedView, MouseButton, MouseDownEvent,
-    ParentElement, Pixels, Point, View, VisualContext, WindowContext,
+    overlay, AnchorCorner, AnyElement, Bounds, DismissEvent, DispatchPhase, Element,
+    ElementContext, ElementId, InteractiveBounds, IntoElement, LayoutId, ManagedView, MouseButton,
+    MouseDownEvent, ParentElement, Pixels, Point, View, VisualContext, WindowContext,
 };
 
 pub struct RightClickMenu<M: ManagedView> {
@@ -64,7 +64,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
     fn request_layout(
         &mut self,
         element_state: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (gpui::LayoutId, Self::State) {
         let (menu, position) = if let Some(element_state) = element_state {
             (element_state.menu, element_state.position)
@@ -116,7 +116,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
         &mut self,
         bounds: Bounds<gpui::Pixels>,
         element_state: &mut Self::State,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) {
         if let Some(mut child) = element_state.child_element.take() {
             child.paint(cx);

crates/ui/src/prelude.rs 🔗

@@ -2,9 +2,9 @@
 
 pub use gpui::prelude::*;
 pub use gpui::{
-    div, px, relative, rems, AbsoluteLength, DefiniteLength, Div, Element, ElementId,
-    InteractiveElement, ParentElement, Pixels, Rems, RenderOnce, SharedString, Styled, ViewContext,
-    WindowContext,
+    div, px, relative, rems, AbsoluteLength, DefiniteLength, Div, Element, ElementContext,
+    ElementId, InteractiveElement, ParentElement, Pixels, Rems, RenderOnce, SharedString, Styled,
+    ViewContext, WindowContext,
 };
 
 pub use crate::clickable::*;

crates/vim/src/test.rs 🔗

@@ -7,7 +7,7 @@ use std::time::Duration;
 
 use command_palette::CommandPalette;
 use editor::DisplayPoint;
-use gpui::{Action, KeyBinding};
+use gpui::KeyBinding;
 pub use neovim_backed_binding_test_context::*;
 pub use neovim_backed_test_context::*;
 pub use vim_test_context::*;
@@ -15,7 +15,7 @@ pub use vim_test_context::*;
 use indoc::indoc;
 use search::BufferSearchBar;
 
-use crate::{insert::NormalBefore, motion, normal::InsertLineBelow, state::Mode, ModeIndicator};
+use crate::{insert::NormalBefore, motion, state::Mode, ModeIndicator};
 
 #[gpui::test]
 async fn test_initially_disabled(cx: &mut gpui::TestAppContext) {

crates/workspace/src/pane_group.rs 🔗

@@ -710,7 +710,7 @@ mod element {
             pane_bounds: Bounds<Pixels>,
             axis_bounds: Bounds<Pixels>,
             workspace: WeakView<Workspace>,
-            cx: &mut WindowContext,
+            cx: &mut ElementContext,
         ) {
             let handle_bounds = Bounds {
                 origin: pane_bounds.origin.apply_along(axis, |origin| {
@@ -803,7 +803,7 @@ mod element {
         fn request_layout(
             &mut self,
             state: Option<Self::State>,
-            cx: &mut ui::prelude::WindowContext,
+            cx: &mut ui::prelude::ElementContext,
         ) -> (gpui::LayoutId, Self::State) {
             let mut style = Style::default();
             style.flex_grow = 1.;
@@ -820,7 +820,7 @@ mod element {
             &mut self,
             bounds: gpui::Bounds<ui::prelude::Pixels>,
             state: &mut Self::State,
-            cx: &mut ui::prelude::WindowContext,
+            cx: &mut ui::prelude::ElementContext,
         ) {
             let flexes = self.flexes.lock().clone();
             let len = self.children.len();

crates/workspace/src/workspace.rs 🔗

@@ -26,12 +26,12 @@ use futures::{
 };
 use gpui::{
     actions, canvas, div, impl_actions, point, px, size, Action, AnyElement, AnyModel, AnyView,
-    AnyWeakView, AppContext, AsyncAppContext, AsyncWindowContext, BorrowWindow, Bounds, Context,
-    Div, DragMoveEvent, Element, Entity, EntityId, EventEmitter, FocusHandle, FocusableView,
-    GlobalPixels, InteractiveElement, IntoElement, KeyContext, LayoutId, ManagedView, Model,
-    ModelContext, ParentElement, PathPromptOptions, Pixels, Point, PromptLevel, Render, Size,
-    Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowBounds,
-    WindowContext, WindowHandle, WindowOptions,
+    AnyWeakView, AppContext, AsyncAppContext, AsyncWindowContext, Bounds, Context, Div,
+    DragMoveEvent, Element, ElementContext, Entity, EntityId, EventEmitter, FocusHandle,
+    FocusableView, GlobalPixels, InteractiveElement, IntoElement, KeyContext, LayoutId,
+    ManagedView, Model, ModelContext, ParentElement, PathPromptOptions, Pixels, Point, PromptLevel,
+    Render, Size, Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView,
+    WindowBounds, WindowContext, WindowHandle, WindowOptions,
 };
 use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem};
 use itertools::Itertools;
@@ -3539,9 +3539,14 @@ impl Render for Workspace {
                     .border_b()
                     .border_color(colors.border)
                     .child(
-                        canvas(cx.listener(|workspace, bounds, _| {
-                            workspace.bounds = *bounds;
-                        }))
+                        canvas({
+                            let this = cx.view().clone();
+                            move |bounds, cx| {
+                                this.update(cx, |this, _cx| {
+                                    this.bounds = *bounds;
+                                })
+                            }
+                        })
                         .absolute()
                         .size_full(),
                     )
@@ -4293,7 +4298,7 @@ impl Element for DisconnectedOverlay {
     fn request_layout(
         &mut self,
         _: Option<Self::State>,
-        cx: &mut WindowContext,
+        cx: &mut ElementContext,
     ) -> (LayoutId, Self::State) {
         let mut background = cx.theme().colors().elevated_surface_background;
         background.fade_out(0.2);
@@ -4315,7 +4320,12 @@ impl Element for DisconnectedOverlay {
         (overlay.request_layout(cx), overlay)
     }
 
-    fn paint(&mut self, bounds: Bounds<Pixels>, overlay: &mut Self::State, cx: &mut WindowContext) {
+    fn paint(
+        &mut self,
+        bounds: Bounds<Pixels>,
+        overlay: &mut Self::State,
+        cx: &mut ElementContext,
+    ) {
         cx.with_z_index(u8::MAX, |cx| {
             cx.add_opaque_layer(bounds);
             overlay.paint(cx);