Port the rest of the app

Mikayla 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/window/element_cx.rs         |  25 +-
crates/terminal_view/src/terminal_element.rs |  22 +-
crates/ui/src/components/popover_menu.rs     |   9 
crates/ui/src/components/right_click_menu.rs |  10 
crates/ui/src/prelude.rs                     |   6 
crates/workspace/src/pane_group.rs           |   6 
crates/workspace/src/workspace.rs            |  32 ++-
11 files changed, 207 insertions(+), 164 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/window/element_cx.rs 🔗

@@ -30,10 +30,7 @@ pub struct ElementContext<'a> {
 }
 
 impl<'a> WindowContext<'a> {
-    pub(crate) fn with_element_context<R>(
-        &mut self,
-        f: impl FnOnce(&mut ElementContext) -> R,
-    ) -> R {
+    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),
         })
@@ -176,7 +173,7 @@ impl<'a> ElementContext<'a> {
     /// 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(crate) fn with_element_id<R>(
+    pub fn with_element_id<R>(
         &mut self,
         id: Option<impl Into<ElementId>>,
         f: impl FnOnce(&mut Self) -> R,
@@ -195,7 +192,7 @@ impl<'a> ElementContext<'a> {
 
     /// Invoke the given function with the given content mask after intersecting it
     /// with the current mask.
-    pub(crate) fn with_content_mask<R>(
+    pub fn with_content_mask<R>(
         &mut self,
         mask: Option<ContentMask<Pixels>>,
         f: impl FnOnce(&mut Self) -> R,
@@ -213,7 +210,7 @@ impl<'a> ElementContext<'a> {
 
     /// Invoke the given function with the content mask reset to that
     /// of the window.
-    pub(crate) fn break_content_mask<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
+    pub fn break_content_mask<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
         let mask = ContentMask {
             bounds: Bounds {
                 origin: Point::default(),
@@ -238,7 +235,7 @@ impl<'a> ElementContext<'a> {
 
     /// 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(crate) fn with_z_index<R>(&mut self, z_index: u8, f: impl FnOnce(&mut Self) -> R) -> R {
+    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(
@@ -255,7 +252,7 @@ impl<'a> ElementContext<'a> {
 
     /// Updates the global element offset relative to the current offset. This is used to implement
     /// scrolling.
-    pub(crate) fn with_element_offset<R>(
+    pub fn with_element_offset<R>(
         &mut self,
         offset: Point<Pixels>,
         f: impl FnOnce(&mut Self) -> R,
@@ -270,7 +267,7 @@ impl<'a> ElementContext<'a> {
 
     /// Updates the global element offset based on the given offset. This is used to implement
     /// drag handles and other manual painting of elements.
-    pub(crate) fn with_absolute_element_offset<R>(
+    pub fn with_absolute_element_offset<R>(
         &mut self,
         offset: Point<Pixels>,
         f: impl FnOnce(&mut Self) -> R,
@@ -285,7 +282,7 @@ impl<'a> ElementContext<'a> {
     }
 
     /// Obtain the current element offset.
-    pub(crate) fn element_offset(&self) -> Point<Pixels> {
+    pub fn element_offset(&self) -> Point<Pixels> {
         self.window()
             .next_frame
             .element_offset_stack
@@ -295,7 +292,7 @@ impl<'a> ElementContext<'a> {
     }
 
     /// Obtain the current content mask.
-    pub(crate) fn content_mask(&self) -> ContentMask<Pixels> {
+    pub fn content_mask(&self) -> ContentMask<Pixels> {
         self.window()
             .next_frame
             .content_mask_stack
@@ -311,7 +308,7 @@ impl<'a> ElementContext<'a> {
 
     /// 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(crate) fn rem_size(&self) -> Pixels {
+    pub fn rem_size(&self) -> Pixels {
         self.window().rem_size
     }
 
@@ -319,7 +316,7 @@ impl<'a> ElementContext<'a> {
     /// 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>(
+    pub fn with_element_state<S, R>(
         &mut self,
         id: ElementId,
         f: impl FnOnce(Option<S>, &mut Self) -> (R, S),

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(
@@ -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/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);