Merge `ElementContext` into `WindowContext` (#10979)

Antonio Scandurra and Nathan Sobo created

The new `ElementContext` was originally introduced to ensure the element
APIs could only be used inside of elements. Unfortunately, there were
many places where some of those APIs needed to be used, so
`WindowContext::with_element_context` was introduced, which defeated the
original safety purposes of having a specific context for elements.

This pull request merges `ElementContext` into `WindowContext` and adds
(debug) runtime checks to APIs that can only be used during certain
phases of element drawing.

Release Notes:

- N/A

---------

Co-authored-by: Nathan Sobo <nathan@zed.dev>

Change summary

crates/diagnostics/src/diagnostics.rs        |  711 ++++----
crates/editor/src/blame_entry_tooltip.rs     |   20 
crates/editor/src/display_map/block_map.rs   |    6 
crates/editor/src/element.rs                 |  245 +-
crates/gpui/src/app/test_context.rs          |   40 
crates/gpui/src/element.rs                   |   84 
crates/gpui/src/elements/anchored.rs         |   10 
crates/gpui/src/elements/animation.rs        |    6 
crates/gpui/src/elements/canvas.rs           |   16 
crates/gpui/src/elements/deferred.rs         |    8 
crates/gpui/src/elements/div.rs              |   68 
crates/gpui/src/elements/img.rs              |   14 
crates/gpui/src/elements/list.rs             |   22 
crates/gpui/src/elements/svg.rs              |   12 
crates/gpui/src/elements/text.rs             |   35 
crates/gpui/src/elements/uniform_list.rs     |   14 
crates/gpui/src/key_dispatch.rs              |    9 
crates/gpui/src/style.rs                     |   10 
crates/gpui/src/text_system/line.rs          |   10 
crates/gpui/src/view.rs                      |   26 
crates/gpui/src/window.rs                    | 1652 +++++++++++++++++++++
crates/gpui/src/window/element_cx.rs         | 1554 --------------------
crates/image_viewer/src/image_viewer.rs      |    2 
crates/terminal_view/src/terminal_element.rs |   24 
crates/ui/src/components/popover_menu.rs     |   16 
crates/ui/src/components/right_click_menu.rs |   46 
crates/ui/src/prelude.rs                     |    6 
crates/workspace/src/pane_group.rs           |    8 
crates/workspace/src/workspace.rs            |   12 
29 files changed, 2,354 insertions(+), 2,332 deletions(-)

Detailed changes

crates/diagnostics/src/diagnostics.rs 🔗

@@ -912,7 +912,7 @@ mod tests {
         display_map::{BlockContext, TransformBlock},
         DisplayPoint, GutterDimensions,
     };
-    use gpui::{px, Stateful, TestAppContext, VisualTestContext, WindowContext};
+    use gpui::{px, AvailableSpace, Stateful, TestAppContext, VisualTestContext};
     use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, PointUtf16, Unclipped};
     use project::FakeFs;
     use serde_json::json;
@@ -1049,67 +1049,66 @@ mod tests {
                 cx,
             )
         });
+        let editor = view.update(cx, |view, _| view.editor.clone());
 
         view.next_notification(cx).await;
-        view.update(cx, |view, cx| {
-            assert_eq!(
-                editor_blocks(&view.editor, cx),
-                [
-                    (0, "path header block".into()),
-                    (2, "diagnostic header".into()),
-                    (15, "collapsed context".into()),
-                    (16, "diagnostic header".into()),
-                    (25, "collapsed context".into()),
-                ]
-            );
+        assert_eq!(
+            editor_blocks(&editor, cx),
+            [
+                (0, "path header block".into()),
+                (2, "diagnostic header".into()),
+                (15, "collapsed context".into()),
+                (16, "diagnostic header".into()),
+                (25, "collapsed context".into()),
+            ]
+        );
+        assert_eq!(
+            editor.update(cx, |editor, cx| editor.display_text(cx)),
+            concat!(
+                //
+                // main.rs
+                //
+                "\n", // filename
+                "\n", // padding
+                // diagnostic group 1
+                "\n", // primary message
+                "\n", // padding
+                "    let x = vec![];\n",
+                "    let y = vec![];\n",
+                "\n", // supporting diagnostic
+                "    a(x);\n",
+                "    b(y);\n",
+                "\n", // supporting diagnostic
+                "    // comment 1\n",
+                "    // comment 2\n",
+                "    c(y);\n",
+                "\n", // supporting diagnostic
+                "    d(x);\n",
+                "\n", // context ellipsis
+                // diagnostic group 2
+                "\n", // primary message
+                "\n", // padding
+                "fn main() {\n",
+                "    let x = vec![];\n",
+                "\n", // supporting diagnostic
+                "    let y = vec![];\n",
+                "    a(x);\n",
+                "\n", // supporting diagnostic
+                "    b(y);\n",
+                "\n", // context ellipsis
+                "    c(y);\n",
+                "    d(x);\n",
+                "\n", // supporting diagnostic
+                "}"
+            )
+        );
+
+        // Cursor is at the first diagnostic
+        editor.update(cx, |editor, cx| {
             assert_eq!(
-                view.editor.update(cx, |editor, cx| editor.display_text(cx)),
-                concat!(
-                    //
-                    // main.rs
-                    //
-                    "\n", // filename
-                    "\n", // padding
-                    // diagnostic group 1
-                    "\n", // primary message
-                    "\n", // padding
-                    "    let x = vec![];\n",
-                    "    let y = vec![];\n",
-                    "\n", // supporting diagnostic
-                    "    a(x);\n",
-                    "    b(y);\n",
-                    "\n", // supporting diagnostic
-                    "    // comment 1\n",
-                    "    // comment 2\n",
-                    "    c(y);\n",
-                    "\n", // supporting diagnostic
-                    "    d(x);\n",
-                    "\n", // context ellipsis
-                    // diagnostic group 2
-                    "\n", // primary message
-                    "\n", // padding
-                    "fn main() {\n",
-                    "    let x = vec![];\n",
-                    "\n", // supporting diagnostic
-                    "    let y = vec![];\n",
-                    "    a(x);\n",
-                    "\n", // supporting diagnostic
-                    "    b(y);\n",
-                    "\n", // context ellipsis
-                    "    c(y);\n",
-                    "    d(x);\n",
-                    "\n", // supporting diagnostic
-                    "}"
-                )
+                editor.selections.display_ranges(cx),
+                [DisplayPoint::new(12, 6)..DisplayPoint::new(12, 6)]
             );
-
-            // Cursor is at the first diagnostic
-            view.editor.update(cx, |editor, cx| {
-                assert_eq!(
-                    editor.selections.display_ranges(cx),
-                    [DisplayPoint::new(12, 6)..DisplayPoint::new(12, 6)]
-                );
-            });
         });
 
         // Diagnostics are added for another earlier path.
@@ -1138,78 +1137,77 @@ mod tests {
         });
 
         view.next_notification(cx).await;
-        view.update(cx, |view, cx| {
-            assert_eq!(
-                editor_blocks(&view.editor, cx),
-                [
-                    (0, "path header block".into()),
-                    (2, "diagnostic header".into()),
-                    (7, "path header block".into()),
-                    (9, "diagnostic header".into()),
-                    (22, "collapsed context".into()),
-                    (23, "diagnostic header".into()),
-                    (32, "collapsed context".into()),
-                ]
-            );
+        assert_eq!(
+            editor_blocks(&editor, cx),
+            [
+                (0, "path header block".into()),
+                (2, "diagnostic header".into()),
+                (7, "path header block".into()),
+                (9, "diagnostic header".into()),
+                (22, "collapsed context".into()),
+                (23, "diagnostic header".into()),
+                (32, "collapsed context".into()),
+            ]
+        );
+
+        assert_eq!(
+            editor.update(cx, |editor, cx| editor.display_text(cx)),
+            concat!(
+                //
+                // consts.rs
+                //
+                "\n", // filename
+                "\n", // padding
+                // diagnostic group 1
+                "\n", // primary message
+                "\n", // padding
+                "const a: i32 = 'a';\n",
+                "\n", // supporting diagnostic
+                "const b: i32 = c;\n",
+                //
+                // main.rs
+                //
+                "\n", // filename
+                "\n", // padding
+                // diagnostic group 1
+                "\n", // primary message
+                "\n", // padding
+                "    let x = vec![];\n",
+                "    let y = vec![];\n",
+                "\n", // supporting diagnostic
+                "    a(x);\n",
+                "    b(y);\n",
+                "\n", // supporting diagnostic
+                "    // comment 1\n",
+                "    // comment 2\n",
+                "    c(y);\n",
+                "\n", // supporting diagnostic
+                "    d(x);\n",
+                "\n", // collapsed context
+                // diagnostic group 2
+                "\n", // primary message
+                "\n", // filename
+                "fn main() {\n",
+                "    let x = vec![];\n",
+                "\n", // supporting diagnostic
+                "    let y = vec![];\n",
+                "    a(x);\n",
+                "\n", // supporting diagnostic
+                "    b(y);\n",
+                "\n", // context ellipsis
+                "    c(y);\n",
+                "    d(x);\n",
+                "\n", // supporting diagnostic
+                "}"
+            )
+        );
+
+        // Cursor keeps its position.
+        editor.update(cx, |editor, cx| {
             assert_eq!(
-                view.editor.update(cx, |editor, cx| editor.display_text(cx)),
-                concat!(
-                    //
-                    // consts.rs
-                    //
-                    "\n", // filename
-                    "\n", // padding
-                    // diagnostic group 1
-                    "\n", // primary message
-                    "\n", // padding
-                    "const a: i32 = 'a';\n",
-                    "\n", // supporting diagnostic
-                    "const b: i32 = c;\n",
-                    //
-                    // main.rs
-                    //
-                    "\n", // filename
-                    "\n", // padding
-                    // diagnostic group 1
-                    "\n", // primary message
-                    "\n", // padding
-                    "    let x = vec![];\n",
-                    "    let y = vec![];\n",
-                    "\n", // supporting diagnostic
-                    "    a(x);\n",
-                    "    b(y);\n",
-                    "\n", // supporting diagnostic
-                    "    // comment 1\n",
-                    "    // comment 2\n",
-                    "    c(y);\n",
-                    "\n", // supporting diagnostic
-                    "    d(x);\n",
-                    "\n", // collapsed context
-                    // diagnostic group 2
-                    "\n", // primary message
-                    "\n", // filename
-                    "fn main() {\n",
-                    "    let x = vec![];\n",
-                    "\n", // supporting diagnostic
-                    "    let y = vec![];\n",
-                    "    a(x);\n",
-                    "\n", // supporting diagnostic
-                    "    b(y);\n",
-                    "\n", // context ellipsis
-                    "    c(y);\n",
-                    "    d(x);\n",
-                    "\n", // supporting diagnostic
-                    "}"
-                )
+                editor.selections.display_ranges(cx),
+                [DisplayPoint::new(19, 6)..DisplayPoint::new(19, 6)]
             );
-
-            // Cursor keeps its position.
-            view.editor.update(cx, |editor, cx| {
-                assert_eq!(
-                    editor.selections.display_ranges(cx),
-                    [DisplayPoint::new(19, 6)..DisplayPoint::new(19, 6)]
-                );
-            });
         });
 
         // Diagnostics are added to the first path
@@ -1254,80 +1252,79 @@ mod tests {
         });
 
         view.next_notification(cx).await;
-        view.update(cx, |view, cx| {
-            assert_eq!(
-                editor_blocks(&view.editor, cx),
-                [
-                    (0, "path header block".into()),
-                    (2, "diagnostic header".into()),
-                    (7, "collapsed context".into()),
-                    (8, "diagnostic header".into()),
-                    (13, "path header block".into()),
-                    (15, "diagnostic header".into()),
-                    (28, "collapsed context".into()),
-                    (29, "diagnostic header".into()),
-                    (38, "collapsed context".into()),
-                ]
-            );
-            assert_eq!(
-                view.editor.update(cx, |editor, cx| editor.display_text(cx)),
-                concat!(
-                    //
-                    // consts.rs
-                    //
-                    "\n", // filename
-                    "\n", // padding
-                    // diagnostic group 1
-                    "\n", // primary message
-                    "\n", // padding
-                    "const a: i32 = 'a';\n",
-                    "\n", // supporting diagnostic
-                    "const b: i32 = c;\n",
-                    "\n", // context ellipsis
-                    // diagnostic group 2
-                    "\n", // primary message
-                    "\n", // padding
-                    "const a: i32 = 'a';\n",
-                    "const b: i32 = c;\n",
-                    "\n", // supporting diagnostic
-                    //
-                    // main.rs
-                    //
-                    "\n", // filename
-                    "\n", // padding
-                    // diagnostic group 1
-                    "\n", // primary message
-                    "\n", // padding
-                    "    let x = vec![];\n",
-                    "    let y = vec![];\n",
-                    "\n", // supporting diagnostic
-                    "    a(x);\n",
-                    "    b(y);\n",
-                    "\n", // supporting diagnostic
-                    "    // comment 1\n",
-                    "    // comment 2\n",
-                    "    c(y);\n",
-                    "\n", // supporting diagnostic
-                    "    d(x);\n",
-                    "\n", // context ellipsis
-                    // diagnostic group 2
-                    "\n", // primary message
-                    "\n", // filename
-                    "fn main() {\n",
-                    "    let x = vec![];\n",
-                    "\n", // supporting diagnostic
-                    "    let y = vec![];\n",
-                    "    a(x);\n",
-                    "\n", // supporting diagnostic
-                    "    b(y);\n",
-                    "\n", // context ellipsis
-                    "    c(y);\n",
-                    "    d(x);\n",
-                    "\n", // supporting diagnostic
-                    "}"
-                )
-            );
-        });
+        assert_eq!(
+            editor_blocks(&editor, cx),
+            [
+                (0, "path header block".into()),
+                (2, "diagnostic header".into()),
+                (7, "collapsed context".into()),
+                (8, "diagnostic header".into()),
+                (13, "path header block".into()),
+                (15, "diagnostic header".into()),
+                (28, "collapsed context".into()),
+                (29, "diagnostic header".into()),
+                (38, "collapsed context".into()),
+            ]
+        );
+
+        assert_eq!(
+            editor.update(cx, |editor, cx| editor.display_text(cx)),
+            concat!(
+                //
+                // consts.rs
+                //
+                "\n", // filename
+                "\n", // padding
+                // diagnostic group 1
+                "\n", // primary message
+                "\n", // padding
+                "const a: i32 = 'a';\n",
+                "\n", // supporting diagnostic
+                "const b: i32 = c;\n",
+                "\n", // context ellipsis
+                // diagnostic group 2
+                "\n", // primary message
+                "\n", // padding
+                "const a: i32 = 'a';\n",
+                "const b: i32 = c;\n",
+                "\n", // supporting diagnostic
+                //
+                // main.rs
+                //
+                "\n", // filename
+                "\n", // padding
+                // diagnostic group 1
+                "\n", // primary message
+                "\n", // padding
+                "    let x = vec![];\n",
+                "    let y = vec![];\n",
+                "\n", // supporting diagnostic
+                "    a(x);\n",
+                "    b(y);\n",
+                "\n", // supporting diagnostic
+                "    // comment 1\n",
+                "    // comment 2\n",
+                "    c(y);\n",
+                "\n", // supporting diagnostic
+                "    d(x);\n",
+                "\n", // context ellipsis
+                // diagnostic group 2
+                "\n", // primary message
+                "\n", // filename
+                "fn main() {\n",
+                "    let x = vec![];\n",
+                "\n", // supporting diagnostic
+                "    let y = vec![];\n",
+                "    a(x);\n",
+                "\n", // supporting diagnostic
+                "    b(y);\n",
+                "\n", // context ellipsis
+                "    c(y);\n",
+                "    d(x);\n",
+                "\n", // supporting diagnostic
+                "}"
+            )
+        );
     }
 
     #[gpui::test]
@@ -1364,6 +1361,7 @@ mod tests {
                 cx,
             )
         });
+        let editor = view.update(cx, |view, _| view.editor.clone());
 
         // Two language servers start updating diagnostics
         project.update(cx, |project, cx| {
@@ -1397,27 +1395,25 @@ mod tests {
 
         // Only the first language server's diagnostics are shown.
         cx.executor().run_until_parked();
-        view.update(cx, |view, cx| {
-            assert_eq!(
-                editor_blocks(&view.editor, cx),
-                [
-                    (0, "path header block".into()),
-                    (2, "diagnostic header".into()),
-                ]
-            );
-            assert_eq!(
-                view.editor.update(cx, |editor, cx| editor.display_text(cx)),
-                concat!(
-                    "\n", // filename
-                    "\n", // padding
-                    // diagnostic group 1
-                    "\n",     // primary message
-                    "\n",     // padding
-                    "a();\n", //
-                    "b();",
-                )
-            );
-        });
+        assert_eq!(
+            editor_blocks(&editor, cx),
+            [
+                (0, "path header block".into()),
+                (2, "diagnostic header".into()),
+            ]
+        );
+        assert_eq!(
+            editor.update(cx, |editor, cx| editor.display_text(cx)),
+            concat!(
+                "\n", // filename
+                "\n", // padding
+                // diagnostic group 1
+                "\n",     // primary message
+                "\n",     // padding
+                "a();\n", //
+                "b();",
+            )
+        );
 
         // The second language server finishes
         project.update(cx, |project, cx| {
@@ -1445,36 +1441,34 @@ mod tests {
 
         // Both language server's diagnostics are shown.
         cx.executor().run_until_parked();
-        view.update(cx, |view, cx| {
-            assert_eq!(
-                editor_blocks(&view.editor, cx),
-                [
-                    (0, "path header block".into()),
-                    (2, "diagnostic header".into()),
-                    (6, "collapsed context".into()),
-                    (7, "diagnostic header".into()),
-                ]
-            );
-            assert_eq!(
-                view.editor.update(cx, |editor, cx| editor.display_text(cx)),
-                concat!(
-                    "\n", // filename
-                    "\n", // padding
-                    // diagnostic group 1
-                    "\n",     // primary message
-                    "\n",     // padding
-                    "a();\n", // location
-                    "b();\n", //
-                    "\n",     // collapsed context
-                    // diagnostic group 2
-                    "\n",     // primary message
-                    "\n",     // padding
-                    "a();\n", // context
-                    "b();\n", //
-                    "c();",   // context
-                )
-            );
-        });
+        assert_eq!(
+            editor_blocks(&editor, cx),
+            [
+                (0, "path header block".into()),
+                (2, "diagnostic header".into()),
+                (6, "collapsed context".into()),
+                (7, "diagnostic header".into()),
+            ]
+        );
+        assert_eq!(
+            editor.update(cx, |editor, cx| editor.display_text(cx)),
+            concat!(
+                "\n", // filename
+                "\n", // padding
+                // diagnostic group 1
+                "\n",     // primary message
+                "\n",     // padding
+                "a();\n", // location
+                "b();\n", //
+                "\n",     // collapsed context
+                // diagnostic group 2
+                "\n",     // primary message
+                "\n",     // padding
+                "a();\n", // context
+                "b();\n", //
+                "c();",   // context
+            )
+        );
 
         // Both language servers start updating diagnostics, and the first server finishes.
         project.update(cx, |project, cx| {
@@ -1513,37 +1507,35 @@ mod tests {
 
         // Only the first language server's diagnostics are updated.
         cx.executor().run_until_parked();
-        view.update(cx, |view, cx| {
-            assert_eq!(
-                editor_blocks(&view.editor, cx),
-                [
-                    (0, "path header block".into()),
-                    (2, "diagnostic header".into()),
-                    (7, "collapsed context".into()),
-                    (8, "diagnostic header".into()),
-                ]
-            );
-            assert_eq!(
-                view.editor.update(cx, |editor, cx| editor.display_text(cx)),
-                concat!(
-                    "\n", // filename
-                    "\n", // padding
-                    // diagnostic group 1
-                    "\n",     // primary message
-                    "\n",     // padding
-                    "a();\n", // location
-                    "b();\n", //
-                    "c();\n", // context
-                    "\n",     // collapsed context
-                    // diagnostic group 2
-                    "\n",     // primary message
-                    "\n",     // padding
-                    "b();\n", // context
-                    "c();\n", //
-                    "d();",   // context
-                )
-            );
-        });
+        assert_eq!(
+            editor_blocks(&editor, cx),
+            [
+                (0, "path header block".into()),
+                (2, "diagnostic header".into()),
+                (7, "collapsed context".into()),
+                (8, "diagnostic header".into()),
+            ]
+        );
+        assert_eq!(
+            editor.update(cx, |editor, cx| editor.display_text(cx)),
+            concat!(
+                "\n", // filename
+                "\n", // padding
+                // diagnostic group 1
+                "\n",     // primary message
+                "\n",     // padding
+                "a();\n", // location
+                "b();\n", //
+                "c();\n", // context
+                "\n",     // collapsed context
+                // diagnostic group 2
+                "\n",     // primary message
+                "\n",     // padding
+                "b();\n", // context
+                "c();\n", //
+                "d();",   // context
+            )
+        );
 
         // The second language server finishes.
         project.update(cx, |project, cx| {
@@ -1571,37 +1563,35 @@ mod tests {
 
         // Both language servers' diagnostics are updated.
         cx.executor().run_until_parked();
-        view.update(cx, |view, cx| {
-            assert_eq!(
-                editor_blocks(&view.editor, cx),
-                [
-                    (0, "path header block".into()),
-                    (2, "diagnostic header".into()),
-                    (7, "collapsed context".into()),
-                    (8, "diagnostic header".into()),
-                ]
-            );
-            assert_eq!(
-                view.editor.update(cx, |editor, cx| editor.display_text(cx)),
-                concat!(
-                    "\n", // filename
-                    "\n", // padding
-                    // diagnostic group 1
-                    "\n",     // primary message
-                    "\n",     // padding
-                    "b();\n", // location
-                    "c();\n", //
-                    "d();\n", // context
-                    "\n",     // collapsed context
-                    // diagnostic group 2
-                    "\n",     // primary message
-                    "\n",     // padding
-                    "c();\n", // context
-                    "d();\n", //
-                    "e();",   // context
-                )
-            );
-        });
+        assert_eq!(
+            editor_blocks(&editor, cx),
+            [
+                (0, "path header block".into()),
+                (2, "diagnostic header".into()),
+                (7, "collapsed context".into()),
+                (8, "diagnostic header".into()),
+            ]
+        );
+        assert_eq!(
+            editor.update(cx, |editor, cx| editor.display_text(cx)),
+            concat!(
+                "\n", // filename
+                "\n", // padding
+                // diagnostic group 1
+                "\n",     // primary message
+                "\n",     // padding
+                "b();\n", // location
+                "c();\n", //
+                "d();\n", // context
+                "\n",     // collapsed context
+                // diagnostic group 2
+                "\n",     // primary message
+                "\n",     // padding
+                "c();\n", // context
+                "d();\n", //
+                "e();",   // context
+            )
+        );
     }
 
     fn init_test(cx: &mut TestAppContext) {
@@ -1618,45 +1608,58 @@ mod tests {
         });
     }
 
-    fn editor_blocks(editor: &View<Editor>, cx: &mut WindowContext) -> Vec<(u32, SharedString)> {
-        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: SharedString = match block {
-                        TransformBlock::Custom(block) => cx.with_element_context({
-                            |cx| -> Option<SharedString> {
-                                let mut element = block.render(&mut BlockContext {
-                                    context: cx,
-                                    anchor_x: px(0.),
-                                    gutter_dimensions: &GutterDimensions::default(),
-                                    line_height: px(0.),
-                                    em_width: px(0.),
-                                    max_width: px(0.),
-                                    block_id: ix,
-                                    editor_style: &editor::EditorStyle::default(),
-                                });
-                                let element = element.downcast_mut::<Stateful<Div>>().unwrap();
-                                element.interactivity().element_id.clone()?.try_into().ok()
-                            }
-                        })?,
-
-                        TransformBlock::ExcerptHeader {
-                            starts_new_buffer, ..
-                        } => {
-                            if *starts_new_buffer {
-                                "path header block".into()
-                            } else {
-                                "collapsed context".into()
-                            }
-                        }
-                    };
+    fn editor_blocks(
+        editor: &View<Editor>,
+        cx: &mut VisualTestContext,
+    ) -> Vec<(u32, SharedString)> {
+        let mut blocks = Vec::new();
+        cx.draw(gpui::Point::default(), AvailableSpace::min_size(), |cx| {
+            editor.update(cx, |editor, cx| {
+                let snapshot = editor.snapshot(cx);
+                blocks.extend(
+                    snapshot
+                        .blocks_in_range(0..snapshot.max_point().row())
+                        .enumerate()
+                        .filter_map(|(ix, (row, block))| {
+                            let name: SharedString = match block {
+                                TransformBlock::Custom(block) => {
+                                    let mut element = block.render(&mut BlockContext {
+                                        context: cx,
+                                        anchor_x: px(0.),
+                                        gutter_dimensions: &GutterDimensions::default(),
+                                        line_height: px(0.),
+                                        em_width: px(0.),
+                                        max_width: px(0.),
+                                        block_id: ix,
+                                        editor_style: &editor::EditorStyle::default(),
+                                    });
+                                    let element = element.downcast_mut::<Stateful<Div>>().unwrap();
+                                    element
+                                        .interactivity()
+                                        .element_id
+                                        .clone()?
+                                        .try_into()
+                                        .ok()?
+                                }
 
-                    Some((row, name))
-                })
-                .collect()
-        })
+                                TransformBlock::ExcerptHeader {
+                                    starts_new_buffer, ..
+                                } => {
+                                    if *starts_new_buffer {
+                                        "path header block".into()
+                                    } else {
+                                        "collapsed context".into()
+                                    }
+                                }
+                            };
+
+                            Some((row, name))
+                        }),
+                )
+            });
+
+            div().into_any()
+        });
+        blocks
     }
 }

crates/editor/src/blame_entry_tooltip.rs 🔗

@@ -39,17 +39,15 @@ impl<'a> CommitAvatar<'a> {
 
         let avatar_url = CommitAvatarAsset::new(remote.clone(), self.sha);
 
-        let element = cx.with_element_context(|cx| {
-            match cx.use_cached_asset::<CommitAvatarAsset>(&avatar_url) {
-                // Loading or no avatar found
-                None | Some(None) => Icon::new(IconName::Person)
-                    .color(Color::Muted)
-                    .into_element()
-                    .into_any(),
-                // Found
-                Some(Some(url)) => Avatar::new(url.to_string()).into_element().into_any(),
-            }
-        });
+        let element = match cx.use_cached_asset::<CommitAvatarAsset>(&avatar_url) {
+            // Loading or no avatar found
+            None | Some(None) => Icon::new(IconName::Person)
+                .color(Color::Muted)
+                .into_element()
+                .into_any(),
+            // Found
+            Some(Some(url)) => Avatar::new(url.to_string()).into_element().into_any(),
+        };
         Some(element)
     }
 }

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

@@ -4,7 +4,7 @@ use super::{
 };
 use crate::{EditorStyle, GutterDimensions};
 use collections::{Bound, HashMap, HashSet};
-use gpui::{AnyElement, ElementContext, Pixels};
+use gpui::{AnyElement, Pixels, WindowContext};
 use language::{BufferSnapshot, Chunk, Patch, Point};
 use multi_buffer::{Anchor, ExcerptId, ExcerptRange, ToPoint as _};
 use parking_lot::Mutex;
@@ -82,7 +82,7 @@ pub enum BlockStyle {
 }
 
 pub struct BlockContext<'a, 'b> {
-    pub context: &'b mut ElementContext<'a>,
+    pub context: &'b mut WindowContext<'a>,
     pub anchor_x: Pixels,
     pub max_width: Pixels,
     pub gutter_dimensions: &'b GutterDimensions,
@@ -934,7 +934,7 @@ impl BlockDisposition {
 }
 
 impl<'a> Deref for BlockContext<'a, '_> {
-    type Target = ElementContext<'a>;
+    type Target = WindowContext<'a>;
 
     fn deref(&self) -> &Self::Target {
         self.context

crates/editor/src/element.rs 🔗

@@ -23,12 +23,11 @@ use git::{blame::BlameEntry, diff::DiffHunkStatus, Oid};
 use gpui::{
     anchored, deferred, div, fill, outline, point, px, quad, relative, size, svg,
     transparent_black, Action, AnchorCorner, AnyElement, AvailableSpace, Bounds, ClipboardItem,
-    ContentMask, Corners, CursorStyle, DispatchPhase, Edges, Element, ElementContext,
-    ElementInputHandler, Entity, Hitbox, Hsla, InteractiveElement, IntoElement,
-    ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad,
-    ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine, SharedString, Size, Stateful,
-    StatefulInteractiveElement, Style, Styled, TextRun, TextStyle, TextStyleRefinement, View,
-    ViewContext, WeakView, WindowContext,
+    ContentMask, Corners, CursorStyle, DispatchPhase, Edges, Element, ElementInputHandler, Hitbox,
+    Hsla, InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton, MouseDownEvent,
+    MouseMoveEvent, MouseUpEvent, PaintQuad, ParentElement, Pixels, ScrollDelta, ScrollWheelEvent,
+    ShapedLine, SharedString, Size, Stateful, StatefulInteractiveElement, Style, Styled, TextRun,
+    TextStyle, TextStyleRefinement, View, ViewContext, WeakView, WindowContext,
 };
 use itertools::Itertools;
 use language::language_settings::ShowWhitespaceSetting;
@@ -367,7 +366,7 @@ impl EditorElement {
         register_action(view, cx, Editor::open_active_item_in_terminal)
     }
 
-    fn register_key_listeners(&self, cx: &mut ElementContext, layout: &EditorLayout) {
+    fn register_key_listeners(&self, cx: &mut WindowContext, layout: &EditorLayout) {
         let position_map = layout.position_map.clone();
         cx.on_key_event({
             let editor = self.editor.clone();
@@ -691,7 +690,7 @@ impl EditorElement {
         snapshot: &EditorSnapshot,
         start_row: u32,
         end_row: u32,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> (
         Vec<(PlayerColor, Vec<SelectionLayout>)>,
         BTreeMap<u32, bool>,
@@ -819,7 +818,7 @@ impl EditorElement {
         scroll_pixel_position: gpui::Point<Pixels>,
         line_height: Pixels,
         line_layouts: &[LineWithInvisibles],
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Vec<FoldLayout> {
         snapshot
             .folds_in_range(visible_anchor_range.clone())
@@ -887,7 +886,7 @@ impl EditorElement {
         line_height: Pixels,
         em_width: Pixels,
         autoscroll_containing_element: bool,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Vec<CursorLayout> {
         let mut autoscroll_bounds = None;
         let cursor_layouts = self.editor.update(cx, |editor, cx| {
@@ -993,7 +992,7 @@ impl EditorElement {
                         color: self.style.background,
                         is_top_row: cursor_position.row() == 0,
                     });
-                    cx.with_element_context(|cx| cursor.layout(content_origin, cursor_name, cx));
+                    cursor.layout(content_origin, cursor_name, cx);
                     cursors.push(cursor);
                 }
             }
@@ -1013,7 +1012,7 @@ impl EditorElement {
         bounds: Bounds<Pixels>,
         scroll_position: gpui::Point<f32>,
         rows_per_page: f32,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Option<ScrollbarLayout> {
         let scrollbar_settings = EditorSettings::get_global(cx).scrollbar;
         let show_scrollbars = match scrollbar_settings.show {
@@ -1082,7 +1081,7 @@ impl EditorElement {
         gutter_settings: crate::editor_settings::Gutter,
         scroll_pixel_position: gpui::Point<Pixels>,
         gutter_hitbox: &Hitbox,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Vec<Option<AnyElement>> {
         let mut indicators = self.editor.update(cx, |editor, cx| {
             editor.render_fold_indicators(
@@ -1155,7 +1154,7 @@ impl EditorElement {
         content_origin: gpui::Point<Pixels>,
         scroll_pixel_position: gpui::Point<Pixels>,
         line_height: Pixels,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Option<AnyElement> {
         if !self
             .editor
@@ -1220,7 +1219,7 @@ impl EditorElement {
         line_height: Pixels,
         gutter_hitbox: &Hitbox,
         max_width: Option<Pixels>,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Option<Vec<AnyElement>> {
         if !self
             .editor
@@ -1285,7 +1284,7 @@ impl EditorElement {
         scroll_pixel_position: gpui::Point<Pixels>,
         gutter_dimensions: &GutterDimensions,
         gutter_hitbox: &Hitbox,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Option<AnyElement> {
         let mut active = false;
         let mut button = None;
@@ -1372,7 +1371,7 @@ impl EditorElement {
         active_rows: &BTreeMap<u32, bool>,
         newest_selection_head: Option<DisplayPoint>,
         snapshot: &EditorSnapshot,
-        cx: &ElementContext,
+        cx: &WindowContext,
     ) -> (
         Vec<Option<ShapedLine>>,
         Vec<Option<(FoldStatus, BufferRow, bool)>>,
@@ -1465,7 +1464,7 @@ impl EditorElement {
         rows: Range<u32>,
         line_number_layouts: &[Option<ShapedLine>],
         snapshot: &EditorSnapshot,
-        cx: &ElementContext,
+        cx: &WindowContext,
     ) -> Vec<LineWithInvisibles> {
         if rows.start >= rows.end {
             return Vec::new();
@@ -1530,7 +1529,7 @@ impl EditorElement {
         text_x: Pixels,
         line_height: Pixels,
         line_layouts: &[LineWithInvisibles],
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Vec<BlockLayout> {
         let mut block_id = 0;
         let (fixed_blocks, non_fixed_blocks) = snapshot
@@ -1544,7 +1543,7 @@ impl EditorElement {
                             available_space: Size<AvailableSpace>,
                             block_id: usize,
                             block_row_start: u32,
-                            cx: &mut ElementContext| {
+                            cx: &mut WindowContext| {
             let mut element = match block {
                 TransformBlock::Custom(block) => {
                     let align_to = block
@@ -1865,7 +1864,7 @@ impl EditorElement {
         hitbox: &Hitbox,
         line_height: Pixels,
         scroll_pixel_position: gpui::Point<Pixels>,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         for block in blocks {
             let mut origin = hitbox.origin
@@ -1893,7 +1892,7 @@ impl EditorElement {
         scroll_pixel_position: gpui::Point<Pixels>,
         line_layouts: &[LineWithInvisibles],
         newest_selection_head: DisplayPoint,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> bool {
         let max_height = cmp::min(
             12. * line_height,
@@ -1933,7 +1932,7 @@ impl EditorElement {
         true
     }
 
-    fn layout_mouse_context_menu(&self, cx: &mut ElementContext) -> Option<AnyElement> {
+    fn layout_mouse_context_menu(&self, cx: &mut WindowContext) -> Option<AnyElement> {
         let mouse_context_menu = self.editor.read(cx).mouse_context_menu.as_ref()?;
         let mut element = deferred(
             anchored()
@@ -1961,7 +1960,7 @@ impl EditorElement {
         line_layouts: &[LineWithInvisibles],
         line_height: Pixels,
         em_width: Pixels,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         struct MeasuredHoverPopover {
             element: AnyElement,
@@ -2021,7 +2020,7 @@ impl EditorElement {
         }
         overall_height += HOVER_POPOVER_GAP;
 
-        fn draw_occluder(width: Pixels, origin: gpui::Point<Pixels>, cx: &mut ElementContext) {
+        fn draw_occluder(width: Pixels, origin: gpui::Point<Pixels>, cx: &mut WindowContext) {
             let mut occlusion = div()
                 .size_full()
                 .occlude()
@@ -2067,7 +2066,7 @@ impl EditorElement {
         }
     }
 
-    fn paint_background(&self, layout: &EditorLayout, cx: &mut ElementContext) {
+    fn paint_background(&self, layout: &EditorLayout, cx: &mut WindowContext) {
         cx.paint_layer(layout.hitbox.bounds, |cx| {
             let scroll_top = layout.position_map.snapshot.scroll_position().y;
             let gutter_bg = cx.theme().colors().editor_gutter_background;
@@ -2188,7 +2187,7 @@ impl EditorElement {
         })
     }
 
-    fn paint_gutter(&mut self, layout: &mut EditorLayout, cx: &mut ElementContext) {
+    fn paint_gutter(&mut self, layout: &mut EditorLayout, cx: &mut WindowContext) {
         let line_height = layout.position_map.line_height;
 
         let scroll_position = layout.position_map.snapshot.scroll_position();
@@ -2236,7 +2235,7 @@ impl EditorElement {
         })
     }
 
-    fn paint_diff_hunks(layout: &EditorLayout, cx: &mut ElementContext) {
+    fn paint_diff_hunks(layout: &EditorLayout, cx: &mut WindowContext) {
         if layout.display_hunks.is_empty() {
             return;
         }
@@ -2342,7 +2341,7 @@ impl EditorElement {
         })
     }
 
-    fn paint_blamed_display_rows(&self, layout: &mut EditorLayout, cx: &mut ElementContext) {
+    fn paint_blamed_display_rows(&self, layout: &mut EditorLayout, cx: &mut WindowContext) {
         let Some(blamed_display_rows) = layout.blamed_display_rows.take() else {
             return;
         };
@@ -2354,7 +2353,7 @@ impl EditorElement {
         })
     }
 
-    fn paint_text(&mut self, layout: &mut EditorLayout, cx: &mut ElementContext) {
+    fn paint_text(&mut self, layout: &mut EditorLayout, cx: &mut WindowContext) {
         cx.with_content_mask(
             Some(ContentMask {
                 bounds: layout.text_hitbox.bounds,
@@ -2386,7 +2385,7 @@ impl EditorElement {
     fn paint_highlights(
         &mut self,
         layout: &mut EditorLayout,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> SmallVec<[Range<DisplayPoint>; 32]> {
         cx.paint_layer(layout.text_hitbox.bounds, |cx| {
             let mut invisible_display_ranges = SmallVec::<[Range<DisplayPoint>; 32]>::new();
@@ -2428,7 +2427,7 @@ impl EditorElement {
         &mut self,
         invisible_display_ranges: &[Range<DisplayPoint>],
         layout: &EditorLayout,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         let whitespace_setting = self
             .editor
@@ -2451,7 +2450,7 @@ impl EditorElement {
         }
     }
 
-    fn paint_redactions(&mut self, layout: &EditorLayout, cx: &mut ElementContext) {
+    fn paint_redactions(&mut self, layout: &EditorLayout, cx: &mut WindowContext) {
         if layout.redacted_ranges.is_empty() {
             return;
         }
@@ -2475,13 +2474,13 @@ impl EditorElement {
         });
     }
 
-    fn paint_cursors(&mut self, layout: &mut EditorLayout, cx: &mut ElementContext) {
+    fn paint_cursors(&mut self, layout: &mut EditorLayout, cx: &mut WindowContext) {
         for cursor in &mut layout.cursors {
             cursor.paint(layout.content_origin, cx);
         }
     }
 
-    fn paint_scrollbar(&mut self, layout: &mut EditorLayout, cx: &mut ElementContext) {
+    fn paint_scrollbar(&mut self, layout: &mut EditorLayout, cx: &mut WindowContext) {
         let Some(scrollbar_layout) = layout.scrollbar_layout.as_ref() else {
             return;
         };
@@ -2617,7 +2616,7 @@ impl EditorElement {
         &self,
         layout: &EditorLayout,
         scrollbar_layout: &ScrollbarLayout,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         self.editor.update(cx, |editor, cx| {
             if !editor.is_singleton(cx)
@@ -2775,7 +2774,7 @@ impl EditorElement {
         corner_radius: Pixels,
         line_end_overshoot: Pixels,
         layout: &EditorLayout,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         let start_row = layout.visible_display_row_range.start;
         let end_row = layout.visible_display_row_range.end;
@@ -2824,7 +2823,7 @@ impl EditorElement {
         }
     }
 
-    fn paint_folds(&mut self, layout: &mut EditorLayout, cx: &mut ElementContext) {
+    fn paint_folds(&mut self, layout: &mut EditorLayout, cx: &mut WindowContext) {
         if layout.folds.is_empty() {
             return;
         }
@@ -2855,7 +2854,7 @@ impl EditorElement {
         })
     }
 
-    fn paint_inline_blame(&mut self, layout: &mut EditorLayout, cx: &mut ElementContext) {
+    fn paint_inline_blame(&mut self, layout: &mut EditorLayout, cx: &mut WindowContext) {
         if let Some(mut inline_blame) = layout.inline_blame.take() {
             cx.paint_layer(layout.text_hitbox.bounds, |cx| {
                 inline_blame.paint(cx);
@@ -2863,19 +2862,19 @@ impl EditorElement {
         }
     }
 
-    fn paint_blocks(&mut self, layout: &mut EditorLayout, cx: &mut ElementContext) {
+    fn paint_blocks(&mut self, layout: &mut EditorLayout, cx: &mut WindowContext) {
         for mut block in layout.blocks.drain(..) {
             block.element.paint(cx);
         }
     }
 
-    fn paint_mouse_context_menu(&mut self, layout: &mut EditorLayout, cx: &mut ElementContext) {
+    fn paint_mouse_context_menu(&mut self, layout: &mut EditorLayout, cx: &mut WindowContext) {
         if let Some(mouse_context_menu) = layout.mouse_context_menu.as_mut() {
             mouse_context_menu.paint(cx);
         }
     }
 
-    fn paint_scroll_wheel_listener(&mut self, layout: &EditorLayout, cx: &mut ElementContext) {
+    fn paint_scroll_wheel_listener(&mut self, layout: &EditorLayout, cx: &mut WindowContext) {
         cx.on_mouse_event({
             let position_map = layout.position_map.clone();
             let editor = self.editor.clone();
@@ -2925,7 +2924,7 @@ impl EditorElement {
         });
     }
 
-    fn paint_mouse_listeners(&mut self, layout: &EditorLayout, cx: &mut ElementContext) {
+    fn paint_mouse_listeners(&mut self, layout: &EditorLayout, cx: &mut WindowContext) {
         self.paint_scroll_wheel_listener(layout, cx);
 
         cx.on_mouse_event({
@@ -3042,7 +3041,7 @@ fn render_inline_blame_entry(
     blame_entry: BlameEntry,
     style: &EditorStyle,
     workspace: Option<WeakView<Workspace>>,
-    cx: &mut ElementContext<'_>,
+    cx: &mut WindowContext<'_>,
 ) -> AnyElement {
     let relative_timestamp = blame_entry_relative_timestamp(&blame_entry, cx);
 
@@ -3073,7 +3072,7 @@ fn render_blame_entry(
     style: &EditorStyle,
     last_used_color: &mut Option<(PlayerColor, Oid)>,
     editor: View<Editor>,
-    cx: &mut ElementContext<'_>,
+    cx: &mut WindowContext<'_>,
 ) -> AnyElement {
     let mut sha_color = cx
         .theme()
@@ -3286,7 +3285,7 @@ impl LineWithInvisibles {
         content_origin: gpui::Point<Pixels>,
         whitespace_setting: ShowWhitespaceSetting,
         selection_ranges: &[Range<DisplayPoint>],
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         let line_height = layout.position_map.line_height;
         let line_y =
@@ -3318,7 +3317,7 @@ impl LineWithInvisibles {
         row: u32,
         line_height: Pixels,
         whitespace_setting: ShowWhitespaceSetting,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         let allowed_invisibles_regions = match whitespace_setting {
             ShowWhitespaceSetting::None => return,
@@ -3365,7 +3364,7 @@ impl Element for EditorElement {
     type RequestLayoutState = ();
     type PrepaintState = EditorLayout;
 
-    fn request_layout(&mut self, cx: &mut ElementContext) -> (gpui::LayoutId, ()) {
+    fn request_layout(&mut self, cx: &mut WindowContext) -> (gpui::LayoutId, ()) {
         self.editor.update(cx, |editor, cx| {
             editor.set_style(self.style.clone(), cx);
 
@@ -3375,36 +3374,31 @@ 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.with_element_context(|cx| cx.request_layout(&style, None))
+                    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.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()
-                            },
-                        )
+                    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.with_element_context(|cx| cx.request_layout(&style, None))
+                    cx.request_layout(&style, None)
                 }
             };
 
@@ -3416,7 +3410,7 @@ impl Element for EditorElement {
         &mut self,
         bounds: Bounds<Pixels>,
         _: &mut Self::RequestLayoutState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Self::PrepaintState {
         let text_style = TextStyleRefinement {
             font_size: Some(self.style.text.font_size),
@@ -3847,13 +3841,12 @@ impl Element for EditorElement {
         bounds: Bounds<gpui::Pixels>,
         _: &mut Self::RequestLayoutState,
         layout: &mut Self::PrepaintState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         let focus_handle = self.editor.focus_handle(cx);
         let key_context = self.editor.read(cx).key_context(cx);
         cx.set_focus_handle(&focus_handle);
         cx.set_key_context(key_context);
-        cx.set_view_id(self.editor.entity_id());
         cx.handle_input(
             &focus_handle,
             ElementInputHandler::new(bounds, self.editor.clone()),
@@ -4206,7 +4199,7 @@ impl CursorLayout {
         &mut self,
         origin: gpui::Point<Pixels>,
         cursor_name: Option<CursorName>,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         if let Some(cursor_name) = cursor_name {
             let bounds = self.bounds(origin);
@@ -4236,7 +4229,7 @@ impl CursorLayout {
         }
     }
 
-    pub fn paint(&mut self, origin: gpui::Point<Pixels>, cx: &mut ElementContext) {
+    pub fn paint(&mut self, origin: gpui::Point<Pixels>, cx: &mut WindowContext) {
         let bounds = self.bounds(origin);
 
         //Draw background or border quad
@@ -4280,7 +4273,7 @@ pub struct HighlightedRangeLine {
 }
 
 impl HighlightedRange {
-    pub fn paint(&self, bounds: Bounds<Pixels>, cx: &mut ElementContext) {
+    pub fn paint(&self, bounds: Bounds<Pixels>, cx: &mut WindowContext) {
         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(
@@ -4299,7 +4292,7 @@ impl HighlightedRange {
         start_y: Pixels,
         lines: &[HighlightedRangeLine],
         _bounds: Bounds<Pixels>,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         if lines.is_empty() {
             return;
@@ -4416,7 +4409,7 @@ mod tests {
         editor_tests::{init_test, update_test_language_settings},
         Editor, MultiBuffer,
     };
-    use gpui::TestAppContext;
+    use gpui::{TestAppContext, VisualTestContext};
     use language::language_settings;
     use log::info;
     use std::num::NonZeroU32;
@@ -4437,18 +4430,16 @@ mod tests {
 
         let layouts = cx
             .update_window(*window, |_, cx| {
-                cx.with_element_context(|cx| {
-                    element
-                        .layout_line_numbers(
-                            0..6,
-                            (0..6).map(Some),
-                            &Default::default(),
-                            Some(DisplayPoint::new(0, 0)),
-                            &snapshot,
-                            cx,
-                        )
-                        .0
-                })
+                element
+                    .layout_line_numbers(
+                        0..6,
+                        (0..6).map(Some),
+                        &Default::default(),
+                        Some(DisplayPoint::new(0, 0)),
+                        &snapshot,
+                        cx,
+                    )
+                    .0
             })
             .unwrap();
         assert_eq!(layouts.len(), 6);
@@ -4487,9 +4478,9 @@ mod tests {
             let buffer = MultiBuffer::build_simple(&(sample_text(6, 6, 'a') + "\n"), cx);
             Editor::new(EditorMode::Full, buffer, None, cx)
         });
+        let cx = &mut VisualTestContext::from_window(*window, cx);
         let editor = window.root(cx).unwrap();
         let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
-        let mut element = EditorElement::new(&editor, style);
 
         window
             .update(cx, |editor, cx| {
@@ -4503,20 +4494,10 @@ mod tests {
                 });
             })
             .unwrap();
-        let state = cx
-            .update_window(window.into(), |_view, cx| {
-                cx.with_element_context(|cx| {
-                    element.prepaint(
-                        Bounds {
-                            origin: point(px(500.), px(500.)),
-                            size: size(px(500.), px(500.)),
-                        },
-                        &mut (),
-                        cx,
-                    )
-                })
-            })
-            .unwrap();
+
+        let (_, state) = cx.draw(point(px(500.), px(500.)), size(px(500.), px(500.)), |_| {
+            EditorElement::new(&editor, style)
+        });
 
         assert_eq!(state.selections.len(), 1);
         let local_selections = &state.selections[0].1;
@@ -4587,7 +4568,6 @@ mod tests {
         });
         let editor = window.root(cx).unwrap();
         let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
-        let mut element = EditorElement::new(&editor, style);
         let _state = window.update(cx, |editor, cx| {
             editor.cursor_shape = CursorShape::Block;
             editor.change_selections(None, cx, |s| {
@@ -4598,20 +4578,9 @@ mod tests {
             });
         });
 
-        let state = cx
-            .update_window(window.into(), |_view, cx| {
-                cx.with_element_context(|cx| {
-                    element.prepaint(
-                        Bounds {
-                            origin: point(px(500.), px(500.)),
-                            size: size(px(500.), px(500.)),
-                        },
-                        &mut (),
-                        cx,
-                    )
-                })
-            })
-            .unwrap();
+        let (_, state) = cx.draw(point(px(500.), px(500.)), size(px(500.), px(500.)), |_| {
+            EditorElement::new(&editor, style)
+        });
         assert_eq!(state.selections.len(), 1);
         let local_selections = &state.selections[0].1;
         assert_eq!(local_selections.len(), 2);
@@ -4640,6 +4609,7 @@ mod tests {
             let buffer = MultiBuffer::build_simple("", cx);
             Editor::new(EditorMode::Full, buffer, None, cx)
         });
+        let cx = &mut VisualTestContext::from_window(*window, cx);
         let editor = window.root(cx).unwrap();
         let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
         window
@@ -4662,22 +4632,9 @@ mod tests {
             })
             .unwrap();
 
-        let mut element = EditorElement::new(&editor, style);
-        let state = cx
-            .update_window(window.into(), |_view, cx| {
-                cx.with_element_context(|cx| {
-                    element.prepaint(
-                        Bounds {
-                            origin: point(px(500.), px(500.)),
-                            size: size(px(500.), px(500.)),
-                        },
-                        &mut (),
-                        cx,
-                    )
-                })
-            })
-            .unwrap();
-
+        let (_, state) = cx.draw(point(px(500.), px(500.)), size(px(500.), px(500.)), |_| {
+            EditorElement::new(&editor, style)
+        });
         assert_eq!(state.position_map.line_layouts.len(), 4);
         assert_eq!(
             state
@@ -4850,31 +4807,19 @@ mod tests {
             let buffer = MultiBuffer::build_simple(&input_text, cx);
             Editor::new(editor_mode, buffer, None, cx)
         });
+        let cx = &mut VisualTestContext::from_window(*window, cx);
         let editor = window.root(cx).unwrap();
         let style = cx.update(|cx| editor.read(cx).style().unwrap().clone());
-        let mut element = EditorElement::new(&editor, style);
         window
             .update(cx, |editor, cx| {
                 editor.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx);
                 editor.set_wrap_width(Some(editor_width), cx);
             })
             .unwrap();
-        let layout_state = cx
-            .update_window(window.into(), |_, cx| {
-                cx.with_element_context(|cx| {
-                    element.prepaint(
-                        Bounds {
-                            origin: point(px(500.), px(500.)),
-                            size: size(px(500.), px(500.)),
-                        },
-                        &mut (),
-                        cx,
-                    )
-                })
-            })
-            .unwrap();
-
-        layout_state
+        let (_, state) = cx.draw(point(px(500.), px(500.)), size(px(500.), px(500.)), |_| {
+            EditorElement::new(&editor, style)
+        });
+        state
             .position_map
             .line_layouts
             .iter()

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

@@ -1,10 +1,11 @@
 use crate::{
-    Action, AnyElement, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext,
-    AvailableSpace, BackgroundExecutor, BorrowAppContext, Bounds, ClipboardItem, Context, Empty,
-    Entity, EventEmitter, ForegroundExecutor, Global, InputEvent, Keystroke, Model, ModelContext,
-    Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
-    Pixels, Platform, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform, TestWindow,
-    TextSystem, View, ViewContext, VisualContext, WindowContext, WindowHandle, WindowOptions,
+    Action, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext, AvailableSpace,
+    BackgroundExecutor, BorrowAppContext, Bounds, ClipboardItem, Context, DrawPhase, Drawable,
+    Element, Empty, Entity, EventEmitter, ForegroundExecutor, Global, InputEvent, Keystroke, Model,
+    ModelContext, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent,
+    MouseUpEvent, Pixels, Platform, Point, Render, Result, Size, Task, TestDispatcher,
+    TestPlatform, TestWindow, TextSystem, View, ViewContext, VisualContext, WindowContext,
+    WindowHandle, WindowOptions,
 };
 use anyhow::{anyhow, bail};
 use futures::{channel::oneshot, Stream, StreamExt};
@@ -725,21 +726,28 @@ impl VisualTestContext {
     }
 
     /// Draw an element to the window. Useful for simulating events or actions
-    pub fn draw(
+    pub fn draw<E>(
         &mut self,
         origin: Point<Pixels>,
-        space: Size<AvailableSpace>,
-        f: impl FnOnce(&mut WindowContext) -> AnyElement,
-    ) {
+        space: impl Into<Size<AvailableSpace>>,
+        f: impl FnOnce(&mut WindowContext) -> E,
+    ) -> (E::RequestLayoutState, E::PrepaintState)
+    where
+        E: Element,
+    {
         self.update(|cx| {
-            cx.with_element_context(|cx| {
-                let mut element = f(cx);
-                element.layout_as_root(space, cx);
-                cx.with_absolute_element_offset(origin, |cx| element.prepaint(cx));
-                element.paint(cx);
-            });
+            cx.window.draw_phase = DrawPhase::Prepaint;
+            let mut element = Drawable::new(f(cx));
+            element.layout_as_root(space.into(), cx);
+            cx.with_absolute_element_offset(origin, |cx| element.prepaint(cx));
 
+            cx.window.draw_phase = DrawPhase::Paint;
+            let (request_layout_state, prepaint_state) = element.paint(cx);
+
+            cx.window.draw_phase = DrawPhase::None;
             cx.refresh();
+
+            (request_layout_state, prepaint_state)
         })
     }
 

crates/gpui/src/element.rs 🔗

@@ -32,12 +32,12 @@
 //! your own custom layout algorithm or rendering a code editor.
 
 use crate::{
-    util::FluentBuilder, ArenaBox, AvailableSpace, Bounds, DispatchNodeId, ElementContext,
-    ElementId, LayoutId, Pixels, Point, Size, ViewContext, WindowContext, ELEMENT_ARENA,
+    util::FluentBuilder, ArenaBox, AvailableSpace, Bounds, DispatchNodeId, ElementId, LayoutId,
+    Pixels, Point, Size, Style, ViewContext, WindowContext, ELEMENT_ARENA,
 };
 use derive_more::{Deref, DerefMut};
 pub(crate) use smallvec::SmallVec;
-use std::{any::Any, fmt::Debug, mem, ops::DerefMut};
+use std::{any::Any, fmt::Debug, mem};
 
 /// 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.
@@ -54,7 +54,7 @@ pub trait Element: 'static + IntoElement {
 
     /// Before an element can be painted, we need to know where it's going to be and how big it is.
     /// Use this method to request a layout from Taffy and initialize the element's state.
-    fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState);
+    fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState);
 
     /// After laying out an element, we need to commit its bounds to the current frame for hitbox
     /// purposes. The state argument is the same state that was returned from [`Element::request_layout()`].
@@ -62,7 +62,7 @@ pub trait Element: 'static + IntoElement {
         &mut self,
         bounds: Bounds<Pixels>,
         request_layout: &mut Self::RequestLayoutState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Self::PrepaintState;
 
     /// Once layout has been completed, this method will be called to paint the element to the screen.
@@ -72,7 +72,7 @@ pub trait Element: 'static + IntoElement {
         bounds: Bounds<Pixels>,
         request_layout: &mut Self::RequestLayoutState,
         prepaint: &mut Self::PrepaintState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     );
 
     /// Convert this element into a dynamically-typed [`AnyElement`].
@@ -164,18 +164,13 @@ impl<C: RenderOnce> Element for Component<C> {
     type RequestLayoutState = AnyElement;
     type PrepaintState = ();
 
-    fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
-        let mut element = self
-            .0
-            .take()
-            .unwrap()
-            .render(cx.deref_mut())
-            .into_any_element();
+    fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
+        let mut element = self.0.take().unwrap().render(cx).into_any_element();
         let layout_id = element.request_layout(cx);
         (layout_id, element)
     }
 
-    fn prepaint(&mut self, _: Bounds<Pixels>, element: &mut AnyElement, cx: &mut ElementContext) {
+    fn prepaint(&mut self, _: Bounds<Pixels>, element: &mut AnyElement, cx: &mut WindowContext) {
         element.prepaint(cx);
     }
 
@@ -184,7 +179,7 @@ impl<C: RenderOnce> Element for Component<C> {
         _: Bounds<Pixels>,
         element: &mut Self::RequestLayoutState,
         _: &mut Self::PrepaintState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         element.paint(cx)
     }
@@ -205,16 +200,16 @@ pub(crate) struct GlobalElementId(SmallVec<[ElementId; 32]>);
 trait ElementObject {
     fn inner_element(&mut self) -> &mut dyn Any;
 
-    fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId;
+    fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId;
 
-    fn prepaint(&mut self, cx: &mut ElementContext);
+    fn prepaint(&mut self, cx: &mut WindowContext);
 
-    fn paint(&mut self, cx: &mut ElementContext);
+    fn paint(&mut self, cx: &mut WindowContext);
 
     fn layout_as_root(
         &mut self,
         available_space: Size<AvailableSpace>,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Size<Pixels>;
 }
 
@@ -249,14 +244,14 @@ enum ElementDrawPhase<RequestLayoutState, PrepaintState> {
 
 /// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.
 impl<E: Element> Drawable<E> {
-    fn new(element: E) -> Self {
+    pub(crate) fn new(element: E) -> Self {
         Drawable {
             element,
             phase: ElementDrawPhase::Start,
         }
     }
 
-    fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
+    fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
         match mem::take(&mut self.phase) {
             ElementDrawPhase::Start => {
                 let (layout_id, request_layout) = self.element.request_layout(cx);
@@ -270,7 +265,7 @@ impl<E: Element> Drawable<E> {
         }
     }
 
-    fn prepaint(&mut self, cx: &mut ElementContext) {
+    pub(crate) fn prepaint(&mut self, cx: &mut WindowContext) {
         match mem::take(&mut self.phase) {
             ElementDrawPhase::RequestLayoutState {
                 layout_id,
@@ -296,7 +291,10 @@ impl<E: Element> Drawable<E> {
         }
     }
 
-    fn paint(&mut self, cx: &mut ElementContext) -> E::RequestLayoutState {
+    pub(crate) fn paint(
+        &mut self,
+        cx: &mut WindowContext,
+    ) -> (E::RequestLayoutState, E::PrepaintState) {
         match mem::take(&mut self.phase) {
             ElementDrawPhase::PrepaintState {
                 node_id,
@@ -309,16 +307,16 @@ impl<E: Element> Drawable<E> {
                 self.element
                     .paint(bounds, &mut request_layout, &mut prepaint, cx);
                 self.phase = ElementDrawPhase::Painted;
-                request_layout
+                (request_layout, prepaint)
             }
             _ => panic!("must call prepaint before paint"),
         }
     }
 
-    fn layout_as_root(
+    pub(crate) fn layout_as_root(
         &mut self,
         available_space: Size<AvailableSpace>,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Size<Pixels> {
         if matches!(&self.phase, ElementDrawPhase::Start) {
             self.request_layout(cx);
@@ -368,22 +366,22 @@ where
         &mut self.element
     }
 
-    fn request_layout(&mut self, cx: &mut ElementContext) -> LayoutId {
+    fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
         Drawable::request_layout(self, cx)
     }
 
-    fn prepaint(&mut self, cx: &mut ElementContext) {
+    fn prepaint(&mut self, cx: &mut WindowContext) {
         Drawable::prepaint(self, cx);
     }
 
-    fn paint(&mut self, cx: &mut ElementContext) {
+    fn paint(&mut self, cx: &mut WindowContext) {
         Drawable::paint(self, cx);
     }
 
     fn layout_as_root(
         &mut self,
         available_space: Size<AvailableSpace>,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Size<Pixels> {
         Drawable::layout_as_root(self, available_space, cx)
     }
@@ -411,18 +409,18 @@ 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 ElementContext) -> LayoutId {
+    pub fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
         self.0.request_layout(cx)
     }
 
     /// Prepares the element to be painted by storing its bounds, giving it a chance to draw hitboxes and
     /// request autoscroll before the final paint pass is confirmed.
-    pub fn prepaint(&mut self, cx: &mut ElementContext) {
+    pub fn prepaint(&mut self, cx: &mut WindowContext) {
         self.0.prepaint(cx)
     }
 
     /// Paints the element stored in this `AnyElement`.
-    pub fn paint(&mut self, cx: &mut ElementContext) {
+    pub fn paint(&mut self, cx: &mut WindowContext) {
         self.0.paint(cx)
     }
 
@@ -430,13 +428,13 @@ impl AnyElement {
     pub fn layout_as_root(
         &mut self,
         available_space: Size<AvailableSpace>,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Size<Pixels> {
         self.0.layout_as_root(available_space, cx)
     }
 
     /// Prepaints this element at the given absolute origin.
-    pub fn prepaint_at(&mut self, origin: Point<Pixels>, cx: &mut ElementContext) {
+    pub fn prepaint_at(&mut self, origin: Point<Pixels>, cx: &mut WindowContext) {
         cx.with_absolute_element_offset(origin, |cx| self.0.prepaint(cx));
     }
 
@@ -445,7 +443,7 @@ impl AnyElement {
         &mut self,
         origin: Point<Pixels>,
         available_space: Size<AvailableSpace>,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         self.layout_as_root(available_space, cx);
         cx.with_absolute_element_offset(origin, |cx| self.0.prepaint(cx));
@@ -456,7 +454,7 @@ impl Element for AnyElement {
     type RequestLayoutState = ();
     type PrepaintState = ();
 
-    fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+    fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
         let layout_id = self.request_layout(cx);
         (layout_id, ())
     }
@@ -465,7 +463,7 @@ impl Element for AnyElement {
         &mut self,
         _: Bounds<Pixels>,
         _: &mut Self::RequestLayoutState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         self.prepaint(cx)
     }
@@ -475,7 +473,7 @@ impl Element for AnyElement {
         _: Bounds<Pixels>,
         _: &mut Self::RequestLayoutState,
         _: &mut Self::PrepaintState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         self.paint(cx)
     }
@@ -508,15 +506,15 @@ impl Element for Empty {
     type RequestLayoutState = ();
     type PrepaintState = ();
 
-    fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
-        (cx.request_layout(&crate::Style::default(), None), ())
+    fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
+        (cx.request_layout(&Style::default(), None), ())
     }
 
     fn prepaint(
         &mut self,
         _bounds: Bounds<Pixels>,
         _state: &mut Self::RequestLayoutState,
-        _cx: &mut ElementContext,
+        _cx: &mut WindowContext,
     ) {
     }
 
@@ -525,7 +523,7 @@ impl Element for Empty {
         _bounds: Bounds<Pixels>,
         _request_layout: &mut Self::RequestLayoutState,
         _prepaint: &mut Self::PrepaintState,
-        _cx: &mut ElementContext,
+        _cx: &mut WindowContext,
     ) {
     }
 }

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

@@ -2,8 +2,8 @@ use smallvec::SmallVec;
 use taffy::style::{Display, Position};
 
 use crate::{
-    point, AnyElement, Bounds, Element, ElementContext, IntoElement, LayoutId, ParentElement,
-    Pixels, Point, Size, Style,
+    point, AnyElement, Bounds, Element, IntoElement, LayoutId, ParentElement, Pixels, Point, Size,
+    Style, WindowContext,
 };
 
 /// The state that the anchored element element uses to track its children.
@@ -74,7 +74,7 @@ impl Element for Anchored {
 
     fn request_layout(
         &mut self,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> (crate::LayoutId, Self::RequestLayoutState) {
         let child_layout_ids = self
             .children
@@ -97,7 +97,7 @@ impl Element for Anchored {
         &mut self,
         bounds: Bounds<Pixels>,
         request_layout: &mut Self::RequestLayoutState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         if request_layout.child_layout_ids.is_empty() {
             return;
@@ -180,7 +180,7 @@ impl Element for Anchored {
         _bounds: crate::Bounds<crate::Pixels>,
         _request_layout: &mut Self::RequestLayoutState,
         _prepaint: &mut Self::PrepaintState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         for child in &mut self.children {
             child.paint(cx);

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

@@ -91,7 +91,7 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
 
     fn request_layout(
         &mut self,
-        cx: &mut crate::ElementContext,
+        cx: &mut crate::WindowContext,
     ) -> (crate::LayoutId, Self::RequestLayoutState) {
         cx.with_element_state(Some(self.id.clone()), |state, cx| {
             let state = state.unwrap().unwrap_or_else(|| AnimationState {
@@ -138,7 +138,7 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
         &mut self,
         _bounds: crate::Bounds<crate::Pixels>,
         element: &mut Self::RequestLayoutState,
-        cx: &mut crate::ElementContext,
+        cx: &mut crate::WindowContext,
     ) -> Self::PrepaintState {
         element.prepaint(cx);
     }
@@ -148,7 +148,7 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
         _bounds: crate::Bounds<crate::Pixels>,
         element: &mut Self::RequestLayoutState,
         _: &mut Self::PrepaintState,
-        cx: &mut crate::ElementContext,
+        cx: &mut crate::WindowContext,
     ) {
         element.paint(cx);
     }

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

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

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

@@ -1,4 +1,4 @@
-use crate::{AnyElement, Bounds, Element, ElementContext, IntoElement, LayoutId, Pixels};
+use crate::{AnyElement, Bounds, Element, IntoElement, LayoutId, Pixels, WindowContext};
 
 /// Builds a `Deferred` element, which delays the layout and paint of its child.
 pub fn deferred(child: impl IntoElement) -> Deferred {
@@ -29,7 +29,7 @@ impl Element for Deferred {
     type RequestLayoutState = ();
     type PrepaintState = ();
 
-    fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, ()) {
+    fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, ()) {
         let layout_id = self.child.as_mut().unwrap().request_layout(cx);
         (layout_id, ())
     }
@@ -38,7 +38,7 @@ impl Element for Deferred {
         &mut self,
         _bounds: Bounds<Pixels>,
         _request_layout: &mut Self::RequestLayoutState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         let child = self.child.take().unwrap();
         let element_offset = cx.element_offset();
@@ -50,7 +50,7 @@ impl Element for Deferred {
         _bounds: Bounds<Pixels>,
         _request_layout: &mut Self::RequestLayoutState,
         _prepaint: &mut Self::PrepaintState,
-        _cx: &mut ElementContext,
+        _cx: &mut WindowContext,
     ) {
     }
 }

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

@@ -17,11 +17,11 @@
 
 use crate::{
     point, px, size, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, Bounds,
-    ClickEvent, DispatchPhase, Element, ElementContext, ElementId, FocusHandle, Global, Hitbox,
-    HitboxId, IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId,
-    ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
-    ParentElement, Pixels, Point, Render, ScrollWheelEvent, SharedString, Size, Style,
-    StyleRefinement, Styled, Task, TooltipId, View, Visibility, WindowContext,
+    ClickEvent, DispatchPhase, Element, ElementId, FocusHandle, Global, Hitbox, HitboxId,
+    IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, ModifiersChangedEvent,
+    MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point,
+    Render, ScrollWheelEvent, SharedString, Size, Style, StyleRefinement, Styled, Task, TooltipId,
+    View, Visibility, WindowContext,
 };
 use collections::HashMap;
 use refineable::Refineable;
@@ -1123,7 +1123,7 @@ impl Element for Div {
     type RequestLayoutState = DivFrameState;
     type PrepaintState = Option<Hitbox>;
 
-    fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+    fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
         let mut child_layout_ids = SmallVec::new();
         let layout_id = self.interactivity.request_layout(cx, |style, cx| {
             cx.with_text_style(style.text_style().cloned(), |cx| {
@@ -1142,7 +1142,7 @@ impl Element for Div {
         &mut self,
         bounds: Bounds<Pixels>,
         request_layout: &mut Self::RequestLayoutState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Option<Hitbox> {
         let mut child_min = point(Pixels::MAX, Pixels::MAX);
         let mut child_max = Point::default();
@@ -1197,7 +1197,7 @@ impl Element for Div {
         bounds: Bounds<Pixels>,
         _request_layout: &mut Self::RequestLayoutState,
         hitbox: &mut Option<Hitbox>,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         self.interactivity
             .paint(bounds, hitbox.as_ref(), cx, |_style, cx| {
@@ -1276,8 +1276,8 @@ impl Interactivity {
     /// Layout this element according to this interactivity state's configured styles
     pub fn request_layout(
         &mut self,
-        cx: &mut ElementContext,
-        f: impl FnOnce(Style, &mut ElementContext) -> LayoutId,
+        cx: &mut WindowContext,
+        f: impl FnOnce(Style, &mut WindowContext) -> LayoutId,
     ) -> LayoutId {
         cx.with_element_state::<InteractiveElementState, _>(
             self.element_id.clone(),
@@ -1341,8 +1341,8 @@ impl Interactivity {
         &mut self,
         bounds: Bounds<Pixels>,
         content_size: Size<Pixels>,
-        cx: &mut ElementContext,
-        f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut ElementContext) -> R,
+        cx: &mut WindowContext,
+        f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut WindowContext) -> R,
     ) -> R {
         self.content_size = content_size;
         cx.with_element_state::<InteractiveElementState, _>(
@@ -1406,7 +1406,7 @@ impl Interactivity {
         &mut self,
         bounds: Bounds<Pixels>,
         style: &Style,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Point<Pixels> {
         if let Some(scroll_offset) = self.scroll_offset.as_ref() {
             if let Some(scroll_handle) = &self.tracked_scroll_handle {
@@ -1456,8 +1456,8 @@ impl Interactivity {
         &mut self,
         bounds: Bounds<Pixels>,
         hitbox: Option<&Hitbox>,
-        cx: &mut ElementContext,
-        f: impl FnOnce(&Style, &mut ElementContext),
+        cx: &mut WindowContext,
+        f: impl FnOnce(&Style, &mut WindowContext),
     ) {
         self.hovered = hitbox.map(|hitbox| hitbox.is_hovered(cx));
         cx.with_element_state::<InteractiveElementState, _>(
@@ -1482,7 +1482,7 @@ impl Interactivity {
                     return ((), element_state);
                 }
 
-                style.paint(bounds, cx, |cx: &mut ElementContext| {
+                style.paint(bounds, cx, |cx: &mut WindowContext| {
                     cx.with_text_style(style.text_style().cloned(), |cx| {
                         cx.with_content_mask(style.overflow_mask(bounds, cx.rem_size()), |cx| {
                             if let Some(hitbox) = hitbox {
@@ -1521,7 +1521,7 @@ impl Interactivity {
     }
 
     #[cfg(debug_assertions)]
-    fn paint_debug_info(&mut self, hitbox: &Hitbox, style: &Style, cx: &mut ElementContext) {
+    fn paint_debug_info(&mut self, hitbox: &Hitbox, style: &Style, cx: &mut WindowContext) {
         if self.element_id.is_some()
             && (style.debug || style.debug_below || cx.has_global::<crate::DebugBelow>())
             && hitbox.is_hovered(cx)
@@ -1530,7 +1530,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 ElementContext| {
+            let render_debug_text = |cx: &mut WindowContext| {
                 if let Some(text) = cx
                     .text_system()
                     .shape_text(
@@ -1629,7 +1629,7 @@ impl Interactivity {
         &mut self,
         hitbox: &Hitbox,
         element_state: Option<&mut InteractiveElementState>,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         // If this element can be focused, register a mouse down listener
         // that will automatically transfer focus when hitting the element.
@@ -1712,11 +1712,11 @@ impl Interactivity {
 
                                     let mut can_drop = true;
                                     if let Some(predicate) = &can_drop_predicate {
-                                        can_drop = predicate(drag.value.as_ref(), cx.deref_mut());
+                                        can_drop = predicate(drag.value.as_ref(), cx);
                                     }
 
                                     if can_drop {
-                                        listener(drag.value.as_ref(), cx.deref_mut());
+                                        listener(drag.value.as_ref(), cx);
                                         cx.refresh();
                                         cx.stop_propagation();
                                     }
@@ -1840,7 +1840,7 @@ impl Interactivity {
                         *was_hovered = is_hovered;
                         drop(was_hovered);
 
-                        hover_listener(&is_hovered, cx.deref_mut());
+                        hover_listener(&is_hovered, cx);
                     }
                 });
             }
@@ -1969,7 +1969,7 @@ impl Interactivity {
         }
     }
 
-    fn paint_keyboard_listeners(&mut self, cx: &mut ElementContext) {
+    fn paint_keyboard_listeners(&mut self, cx: &mut WindowContext) {
         let key_down_listeners = mem::take(&mut self.key_down_listeners);
         let key_up_listeners = mem::take(&mut self.key_up_listeners);
         let modifiers_changed_listeners = mem::take(&mut self.modifiers_changed_listeners);
@@ -2004,7 +2004,7 @@ impl Interactivity {
         }
     }
 
-    fn paint_hover_group_handler(&self, cx: &mut ElementContext) {
+    fn paint_hover_group_handler(&self, cx: &mut WindowContext) {
         let group_hitbox = self
             .group_hover_style
             .as_ref()
@@ -2021,7 +2021,7 @@ impl Interactivity {
         }
     }
 
-    fn paint_scroll_listener(&self, hitbox: &Hitbox, style: &Style, cx: &mut ElementContext) {
+    fn paint_scroll_listener(&self, hitbox: &Hitbox, style: &Style, cx: &mut WindowContext) {
         if let Some(scroll_offset) = self.scroll_offset.clone() {
             let overflow = style.overflow;
             let line_height = cx.line_height();
@@ -2064,7 +2064,7 @@ impl Interactivity {
     }
 
     /// Compute the visual style for this element, based on the current bounds and the element's state.
-    pub fn compute_style(&self, hitbox: Option<&Hitbox>, cx: &mut ElementContext) -> Style {
+    pub fn compute_style(&self, hitbox: Option<&Hitbox>, cx: &mut WindowContext) -> Style {
         cx.with_element_state(self.element_id.clone(), |element_state, cx| {
             let mut element_state =
                 element_state.map(|element_state| element_state.unwrap_or_default());
@@ -2078,7 +2078,7 @@ impl Interactivity {
         &self,
         hitbox: Option<&Hitbox>,
         element_state: Option<&mut InteractiveElementState>,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Style {
         let mut style = Style::default();
         style.refine(&self.base_style);
@@ -2119,7 +2119,7 @@ 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.deref_mut());
+                    can_drop = can_drop_predicate(drag.value.as_ref(), cx);
                 }
 
                 if can_drop {
@@ -2264,7 +2264,7 @@ where
     type RequestLayoutState = E::RequestLayoutState;
     type PrepaintState = E::PrepaintState;
 
-    fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+    fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
         self.element.request_layout(cx)
     }
 
@@ -2272,7 +2272,7 @@ where
         &mut self,
         bounds: Bounds<Pixels>,
         state: &mut Self::RequestLayoutState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> E::PrepaintState {
         self.element.prepaint(bounds, state, cx)
     }
@@ -2282,7 +2282,7 @@ where
         bounds: Bounds<Pixels>,
         request_layout: &mut Self::RequestLayoutState,
         prepaint: &mut Self::PrepaintState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         self.element.paint(bounds, request_layout, prepaint, cx)
     }
@@ -2347,7 +2347,7 @@ where
     type RequestLayoutState = E::RequestLayoutState;
     type PrepaintState = E::PrepaintState;
 
-    fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+    fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
         self.element.request_layout(cx)
     }
 
@@ -2355,7 +2355,7 @@ where
         &mut self,
         bounds: Bounds<Pixels>,
         state: &mut Self::RequestLayoutState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> E::PrepaintState {
         self.element.prepaint(bounds, state, cx)
     }
@@ -2365,7 +2365,7 @@ where
         bounds: Bounds<Pixels>,
         request_layout: &mut Self::RequestLayoutState,
         prepaint: &mut Self::PrepaintState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         self.element.paint(bounds, request_layout, prepaint, cx);
     }

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

@@ -3,9 +3,9 @@ use std::path::PathBuf;
 use std::sync::Arc;
 
 use crate::{
-    point, px, size, AbsoluteLength, Asset, Bounds, DefiniteLength, DevicePixels, Element,
-    ElementContext, Hitbox, ImageData, InteractiveElement, Interactivity, IntoElement, LayoutId,
-    Length, Pixels, SharedUri, Size, StyleRefinement, Styled, SvgSize, UriOrPath, WindowContext,
+    point, px, size, AbsoluteLength, Asset, Bounds, DefiniteLength, DevicePixels, Element, Hitbox,
+    ImageData, InteractiveElement, Interactivity, IntoElement, LayoutId, Length, Pixels, SharedUri,
+    Size, StyleRefinement, Styled, SvgSize, UriOrPath, WindowContext,
 };
 use futures::{AsyncReadExt, Future};
 use image::{ImageBuffer, ImageError};
@@ -232,7 +232,7 @@ impl Element for Img {
     type RequestLayoutState = ();
     type PrepaintState = Option<Hitbox>;
 
-    fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+    fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
         let layout_id = self.interactivity.request_layout(cx, |mut style, cx| {
             if let Some(data) = self.source.data(cx) {
                 let image_size = data.size();
@@ -260,7 +260,7 @@ impl Element for Img {
         &mut self,
         bounds: Bounds<Pixels>,
         _request_layout: &mut Self::RequestLayoutState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Option<Hitbox> {
         self.interactivity
             .prepaint(bounds, bounds.size, cx, |_, _, hitbox, _| hitbox)
@@ -271,7 +271,7 @@ impl Element for Img {
         bounds: Bounds<Pixels>,
         _: &mut Self::RequestLayoutState,
         hitbox: &mut Self::PrepaintState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         let source = self.source.clone();
         self.interactivity
@@ -319,7 +319,7 @@ impl InteractiveElement for Img {
 }
 
 impl ImageSource {
-    fn data(&self, cx: &mut ElementContext) -> Option<Arc<ImageData>> {
+    fn data(&self, cx: &mut WindowContext) -> Option<Arc<ImageData>> {
         match self {
             ImageSource::Uri(_) | ImageSource::File(_) => {
                 let uri_or_path: UriOrPath = match self {

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

@@ -8,8 +8,8 @@
 
 use crate::{
     point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, DispatchPhase, Edges,
-    Element, ElementContext, FocusHandle, Hitbox, IntoElement, Pixels, Point, ScrollWheelEvent,
-    Size, Style, StyleRefinement, Styled, WindowContext,
+    Element, FocusHandle, Hitbox, IntoElement, Pixels, Point, ScrollWheelEvent, Size, Style,
+    StyleRefinement, Styled, WindowContext,
 };
 use collections::VecDeque;
 use refineable::Refineable as _;
@@ -434,7 +434,7 @@ impl StateInner {
         available_width: Option<Pixels>,
         available_height: Pixels,
         padding: &Edges<Pixels>,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> LayoutItemsResponse {
         let old_items = self.items.clone();
         let mut measured_items = VecDeque::new();
@@ -609,7 +609,7 @@ impl StateInner {
         bounds: Bounds<Pixels>,
         padding: Edges<Pixels>,
         autoscroll: bool,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Result<LayoutItemsResponse, ListOffset> {
         cx.transact(|cx| {
             let mut layout_response =
@@ -706,7 +706,7 @@ impl Element for List {
 
     fn request_layout(
         &mut self,
-        cx: &mut crate::ElementContext,
+        cx: &mut crate::WindowContext,
     ) -> (crate::LayoutId, Self::RequestLayoutState) {
         let layout_id = match self.sizing_behavior {
             ListSizingBehavior::Infer => {
@@ -772,7 +772,7 @@ impl Element for List {
         &mut self,
         bounds: Bounds<Pixels>,
         _: &mut Self::RequestLayoutState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> ListPrepaintState {
         let state = &mut *self.state.0.borrow_mut();
         state.reset = false;
@@ -815,7 +815,7 @@ impl Element for List {
         bounds: Bounds<crate::Pixels>,
         _: &mut Self::RequestLayoutState,
         prepaint: &mut Self::PrepaintState,
-        cx: &mut crate::ElementContext,
+        cx: &mut crate::WindowContext,
     ) {
         cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
             for item in &mut prepaint.layout.item_layouts {
@@ -951,11 +951,9 @@ mod test {
         });
 
         // Paint
-        cx.draw(
-            point(px(0.), px(0.)),
-            size(px(100.), px(20.)).into(),
-            |_| list(state.clone()).w_full().h_full().into_any(),
-        );
+        cx.draw(point(px(0.), px(0.)), size(px(100.), px(20.)), |_| {
+            list(state.clone()).w_full().h_full()
+        });
 
         // Reset
         state.reset(5);

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

@@ -1,7 +1,7 @@
 use crate::{
-    geometry::Negate as _, point, px, radians, size, Bounds, Element, ElementContext, Hitbox,
-    InteractiveElement, Interactivity, IntoElement, LayoutId, Pixels, Point, Radians, SharedString,
-    Size, StyleRefinement, Styled, TransformationMatrix,
+    geometry::Negate as _, point, px, radians, size, Bounds, Element, Hitbox, InteractiveElement,
+    Interactivity, IntoElement, LayoutId, Pixels, Point, Radians, SharedString, Size,
+    StyleRefinement, Styled, TransformationMatrix, WindowContext,
 };
 use util::ResultExt;
 
@@ -40,7 +40,7 @@ impl Element for Svg {
     type RequestLayoutState = ();
     type PrepaintState = Option<Hitbox>;
 
-    fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+    fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
         let layout_id = self
             .interactivity
             .request_layout(cx, |style, cx| cx.request_layout(&style, None));
@@ -51,7 +51,7 @@ impl Element for Svg {
         &mut self,
         bounds: Bounds<Pixels>,
         _request_layout: &mut Self::RequestLayoutState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Option<Hitbox> {
         self.interactivity
             .prepaint(bounds, bounds.size, cx, |_, _, hitbox, _| hitbox)
@@ -62,7 +62,7 @@ impl Element for Svg {
         bounds: Bounds<Pixels>,
         _request_layout: &mut Self::RequestLayoutState,
         hitbox: &mut Option<Hitbox>,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) where
         Self: Sized,
     {

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

@@ -1,8 +1,7 @@
 use crate::{
-    ActiveTooltip, AnyTooltip, AnyView, Bounds, DispatchPhase, Element, ElementContext, ElementId,
-    HighlightStyle, Hitbox, IntoElement, LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
-    Pixels, Point, SharedString, Size, TextRun, TextStyle, WhiteSpace, WindowContext, WrappedLine,
-    TOOLTIP_DELAY,
+    ActiveTooltip, AnyTooltip, AnyView, Bounds, DispatchPhase, Element, ElementId, HighlightStyle,
+    Hitbox, IntoElement, LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Point,
+    SharedString, Size, TextRun, TextStyle, WhiteSpace, WindowContext, WrappedLine, TOOLTIP_DELAY,
 };
 use anyhow::anyhow;
 use parking_lot::{Mutex, MutexGuard};
@@ -20,7 +19,7 @@ impl Element for &'static str {
     type RequestLayoutState = TextState;
     type PrepaintState = ();
 
-    fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+    fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
         let mut state = TextState::default();
         let layout_id = state.layout(SharedString::from(*self), None, cx);
         (layout_id, state)
@@ -30,7 +29,7 @@ impl Element for &'static str {
         &mut self,
         _bounds: Bounds<Pixels>,
         _text_state: &mut Self::RequestLayoutState,
-        _cx: &mut ElementContext,
+        _cx: &mut WindowContext,
     ) {
     }
 
@@ -39,7 +38,7 @@ impl Element for &'static str {
         bounds: Bounds<Pixels>,
         text_state: &mut TextState,
         _: &mut (),
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         text_state.paint(bounds, self, cx)
     }
@@ -65,7 +64,7 @@ impl Element for SharedString {
     type RequestLayoutState = TextState;
     type PrepaintState = ();
 
-    fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+    fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
         let mut state = TextState::default();
         let layout_id = state.layout(self.clone(), None, cx);
         (layout_id, state)
@@ -75,7 +74,7 @@ impl Element for SharedString {
         &mut self,
         _bounds: Bounds<Pixels>,
         _text_state: &mut Self::RequestLayoutState,
-        _cx: &mut ElementContext,
+        _cx: &mut WindowContext,
     ) {
     }
 
@@ -84,7 +83,7 @@ impl Element for SharedString {
         bounds: Bounds<Pixels>,
         text_state: &mut Self::RequestLayoutState,
         _: &mut Self::PrepaintState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         let text_str: &str = self.as_ref();
         text_state.paint(bounds, text_str, cx)
@@ -151,7 +150,7 @@ impl Element for StyledText {
     type RequestLayoutState = TextState;
     type PrepaintState = ();
 
-    fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+    fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
         let mut state = TextState::default();
         let layout_id = state.layout(self.text.clone(), self.runs.take(), cx);
         (layout_id, state)
@@ -161,7 +160,7 @@ impl Element for StyledText {
         &mut self,
         _bounds: Bounds<Pixels>,
         _state: &mut Self::RequestLayoutState,
-        _cx: &mut ElementContext,
+        _cx: &mut WindowContext,
     ) {
     }
 
@@ -170,7 +169,7 @@ impl Element for StyledText {
         bounds: Bounds<Pixels>,
         text_state: &mut Self::RequestLayoutState,
         _: &mut Self::PrepaintState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         text_state.paint(bounds, &self.text, cx)
     }
@@ -204,7 +203,7 @@ impl TextState {
         &mut self,
         text: SharedString,
         runs: Option<Vec<TextRun>>,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> LayoutId {
         let text_style = cx.text_style();
         let font_size = text_style.font_size.to_pixels(cx.rem_size());
@@ -279,7 +278,7 @@ impl TextState {
         layout_id
     }
 
-    fn paint(&mut self, bounds: Bounds<Pixels>, text: &str, cx: &mut ElementContext) {
+    fn paint(&mut self, bounds: Bounds<Pixels>, text: &str, cx: &mut WindowContext) {
         let element_state = self.lock();
         let element_state = element_state
             .as_ref()
@@ -405,7 +404,7 @@ impl Element for InteractiveText {
     type RequestLayoutState = TextState;
     type PrepaintState = Hitbox;
 
-    fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+    fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
         self.text.request_layout(cx)
     }
 
@@ -413,7 +412,7 @@ impl Element for InteractiveText {
         &mut self,
         bounds: Bounds<Pixels>,
         state: &mut Self::RequestLayoutState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Hitbox {
         cx.with_element_state::<InteractiveTextState, _>(
             Some(self.element_id.clone()),
@@ -442,7 +441,7 @@ impl Element for InteractiveText {
         bounds: Bounds<Pixels>,
         text_state: &mut Self::RequestLayoutState,
         hitbox: &mut Hitbox,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         cx.with_element_state::<InteractiveTextState, _>(
             Some(self.element_id.clone()),

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

@@ -5,9 +5,9 @@
 //! elements with uniform height.
 
 use crate::{
-    point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, Element, ElementContext,
-    ElementId, Hitbox, InteractiveElement, Interactivity, IntoElement, LayoutId, Pixels, Render,
-    ScrollHandle, Size, StyleRefinement, Styled, View, ViewContext, WindowContext,
+    point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, Element, ElementId, Hitbox,
+    InteractiveElement, Interactivity, IntoElement, LayoutId, Pixels, Render, ScrollHandle, Size,
+    StyleRefinement, Styled, View, ViewContext, WindowContext,
 };
 use smallvec::SmallVec;
 use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
@@ -107,7 +107,7 @@ impl Element for UniformList {
     type RequestLayoutState = UniformListFrameState;
     type PrepaintState = Option<Hitbox>;
 
-    fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+    fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
         let max_items = self.item_count;
         let item_size = self.measure_item(None, cx);
         let layout_id = self.interactivity.request_layout(cx, |style, cx| {
@@ -141,7 +141,7 @@ impl Element for UniformList {
         &mut self,
         bounds: Bounds<Pixels>,
         frame_state: &mut Self::RequestLayoutState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Option<Hitbox> {
         let style = self.interactivity.compute_style(None, cx);
         let border = style.border_widths.to_pixels(cx.rem_size());
@@ -239,7 +239,7 @@ impl Element for UniformList {
         bounds: Bounds<crate::Pixels>,
         request_layout: &mut Self::RequestLayoutState,
         hitbox: &mut Option<Hitbox>,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         self.interactivity
             .paint(bounds, hitbox.as_ref(), cx, |_, cx| {
@@ -265,7 +265,7 @@ impl UniformList {
         self
     }
 
-    fn measure_item(&self, list_width: Option<Pixels>, cx: &mut ElementContext) -> Size<Pixels> {
+    fn measure_item(&self, list_width: Option<Pixels>, cx: &mut WindowContext) -> Size<Pixels> {
         if self.item_count == 0 {
             return Size::default();
         }

crates/gpui/src/key_dispatch.rs 🔗

@@ -50,9 +50,8 @@
 ///  KeyBinding::new("cmd-k left", pane::SplitLeft, Some("Pane"))
 ///
 use crate::{
-    Action, ActionRegistry, DispatchPhase, ElementContext, EntityId, FocusId, KeyBinding,
-    KeyContext, Keymap, KeymatchResult, Keystroke, KeystrokeMatcher, ModifiersChangedEvent,
-    WindowContext,
+    Action, ActionRegistry, DispatchPhase, EntityId, FocusId, KeyBinding, KeyContext, Keymap,
+    KeymatchResult, Keystroke, KeystrokeMatcher, ModifiersChangedEvent, WindowContext,
 };
 use collections::FxHashMap;
 use smallvec::SmallVec;
@@ -107,8 +106,8 @@ impl ReusedSubtree {
     }
 }
 
-type KeyListener = Rc<dyn Fn(&dyn Any, DispatchPhase, &mut ElementContext)>;
-type ModifiersChangedListener = Rc<dyn Fn(&ModifiersChangedEvent, &mut ElementContext)>;
+type KeyListener = Rc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>;
+type ModifiersChangedListener = Rc<dyn Fn(&ModifiersChangedEvent, &mut WindowContext)>;
 
 #[derive(Clone)]
 pub(crate) struct DispatchActionListener {

crates/gpui/src/style.rs 🔗

@@ -2,9 +2,9 @@ use std::{iter, mem, ops::Range};
 
 use crate::{
     black, phi, point, quad, rems, AbsoluteLength, Bounds, ContentMask, Corners, CornersRefinement,
-    CursorStyle, DefiniteLength, Edges, EdgesRefinement, ElementContext, Font, FontFeatures,
-    FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rgba, SharedString, Size,
-    SizeRefinement, Styled, TextRun,
+    CursorStyle, DefiniteLength, Edges, EdgesRefinement, Font, FontFeatures, FontStyle, FontWeight,
+    Hsla, Length, Pixels, Point, PointRefinement, Rgba, SharedString, Size, SizeRefinement, Styled,
+    TextRun, WindowContext,
 };
 use collections::HashSet;
 use refineable::Refineable;
@@ -391,8 +391,8 @@ impl Style {
     pub fn paint(
         &self,
         bounds: Bounds<Pixels>,
-        cx: &mut ElementContext,
-        continuation: impl FnOnce(&mut ElementContext),
+        cx: &mut WindowContext,
+        continuation: impl FnOnce(&mut WindowContext),
     ) {
         #[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, Bounds, ElementContext, Hsla, LineLayout, Pixels, Point, Result,
-    SharedString, StrikethroughStyle, UnderlineStyle, WrapBoundary, WrappedLineLayout,
+    black, fill, point, px, size, Bounds, Hsla, LineLayout, Pixels, Point, Result, SharedString,
+    StrikethroughStyle, UnderlineStyle, WindowContext, WrapBoundary, WrappedLineLayout,
 };
 use derive_more::{Deref, DerefMut};
 use smallvec::SmallVec;
@@ -48,7 +48,7 @@ impl ShapedLine {
         &self,
         origin: Point<Pixels>,
         line_height: Pixels,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Result<()> {
         paint_line(
             origin,
@@ -86,7 +86,7 @@ impl WrappedLine {
         &self,
         origin: Point<Pixels>,
         line_height: Pixels,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Result<()> {
         paint_line(
             origin,
@@ -107,7 +107,7 @@ fn paint_line(
     line_height: Pixels,
     decoration_runs: &[DecorationRun],
     wrap_boundaries: &[WrapBoundary],
-    cx: &mut ElementContext,
+    cx: &mut WindowContext,
 ) -> Result<()> {
     let line_bounds = Bounds::new(origin, size(layout.width, line_height));
     cx.paint_layer(line_bounds, |cx| {

crates/gpui/src/view.rs 🔗

@@ -1,8 +1,8 @@
 use crate::{
     seal::Sealed, AnyElement, AnyModel, AnyWeakModel, AppContext, Bounds, ContentMask, Element,
-    ElementContext, ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView, IntoElement,
-    LayoutId, Model, PaintIndex, Pixels, PrepaintStateIndex, Render, Style, StyleRefinement,
-    TextStyle, ViewContext, VisualContext, WeakModel,
+    ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView, IntoElement, LayoutId, Model,
+    PaintIndex, Pixels, PrepaintStateIndex, Render, Style, StyleRefinement, TextStyle, ViewContext,
+    VisualContext, WeakModel, WindowContext,
 };
 use anyhow::{Context, Result};
 use refineable::Refineable;
@@ -93,7 +93,7 @@ impl<V: Render> Element for View<V> {
     type RequestLayoutState = AnyElement;
     type PrepaintState = ();
 
-    fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+    fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
         cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
             let mut element = self.update(cx, |view, cx| view.render(cx).into_any_element());
             let layout_id = element.request_layout(cx);
@@ -105,7 +105,7 @@ impl<V: Render> Element for View<V> {
         &mut self,
         _: Bounds<Pixels>,
         element: &mut Self::RequestLayoutState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         cx.set_view_id(self.entity_id());
         cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
@@ -118,7 +118,7 @@ impl<V: Render> Element for View<V> {
         _: Bounds<Pixels>,
         element: &mut Self::RequestLayoutState,
         _: &mut Self::PrepaintState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
             element.paint(cx)
@@ -220,7 +220,7 @@ impl<V> Eq for WeakView<V> {}
 #[derive(Clone, Debug)]
 pub struct AnyView {
     model: AnyModel,
-    render: fn(&AnyView, &mut ElementContext) -> AnyElement,
+    render: fn(&AnyView, &mut WindowContext) -> AnyElement,
     cached_style: Option<StyleRefinement>,
 }
 
@@ -279,7 +279,7 @@ impl Element for AnyView {
     type RequestLayoutState = Option<AnyElement>;
     type PrepaintState = Option<AnyElement>;
 
-    fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+    fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
         if let Some(style) = self.cached_style.as_ref() {
             let mut root_style = Style::default();
             root_style.refine(style);
@@ -298,7 +298,7 @@ impl Element for AnyView {
         &mut self,
         bounds: Bounds<Pixels>,
         element: &mut Self::RequestLayoutState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Option<AnyElement> {
         cx.set_view_id(self.entity_id());
         if self.cached_style.is_some() {
@@ -359,7 +359,7 @@ impl Element for AnyView {
         _bounds: Bounds<Pixels>,
         _: &mut Self::RequestLayoutState,
         element: &mut Self::PrepaintState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         if self.cached_style.is_some() {
             cx.with_element_state::<AnyViewState, _>(
@@ -408,7 +408,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,
-    render: fn(&AnyView, &mut ElementContext) -> AnyElement,
+    render: fn(&AnyView, &mut WindowContext) -> AnyElement,
 }
 
 impl AnyWeakView {
@@ -447,11 +447,11 @@ impl std::fmt::Debug for AnyWeakView {
 }
 
 mod any_view {
-    use crate::{AnyElement, AnyView, ElementContext, IntoElement, Render};
+    use crate::{AnyElement, AnyView, IntoElement, Render, WindowContext};
 
     pub(crate) fn render<V: 'static + Render>(
         view: &AnyView,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> AnyElement {
         let view = view.clone().downcast::<V>().unwrap();
         view.update(cx, |view, cx| view.render(cx).into_any_element())

crates/gpui/src/window.rs 🔗

@@ -1,32 +1,42 @@
 use crate::{
-    point, px, size, transparent_black, Action, AnyDrag, AnyView, AppContext, Arena,
-    AsyncWindowContext, Bounds, Context, Corners, CursorStyle, DevicePixels,
-    DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity,
-    EntityId, EventEmitter, FileDropEvent, Flatten, Global, GlobalElementId, Hsla, KeyBinding,
-    KeyDownEvent, KeyMatch, KeymatchResult, Keystroke, KeystrokeEvent, Model, ModelContext,
-    Modifiers, ModifiersChangedEvent, MouseButton, MouseMoveEvent, MouseUpEvent, Pixels,
-    PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptLevel, Render,
-    ScaledPixels, SharedString, Size, SubscriberSet, Subscription, TaffyLayoutEngine, Task,
-    TextStyle, TextStyleRefinement, View, VisualContext, WeakView, WindowAppearance,
-    WindowBackgroundAppearance, WindowOptions, WindowParams, WindowTextSystem,
+    hash, point, prelude::*, px, size, transparent_black, Action, AnyDrag, AnyElement, AnyTooltip,
+    AnyView, AppContext, Arena, Asset, AsyncWindowContext, AvailableSpace, Bounds, BoxShadow,
+    Context, Corners, CursorStyle, DevicePixels, DispatchActionListener, DispatchNodeId,
+    DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, Flatten,
+    FontId, Global, GlobalElementId, GlyphId, Hsla, ImageData, InputHandler, IsZero, KeyBinding,
+    KeyContext, KeyDownEvent, KeyEvent, KeyMatch, KeymatchResult, Keystroke, KeystrokeEvent,
+    LayoutId, LineLayoutIndex, Model, ModelContext, Modifiers, ModifiersChangedEvent,
+    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, StrikethroughStyle, Style,
+    SubscriberSet, Subscription, TaffyLayoutEngine, Task, TextStyle, TextStyleRefinement,
+    TransformationMatrix, Underline, UnderlineStyle, View, VisualContext, WeakView,
+    WindowAppearance, WindowBackgroundAppearance, WindowOptions, WindowParams, WindowTextSystem,
+    SUBPIXEL_VARIANTS,
 };
 use anyhow::{anyhow, Context as _, Result};
-use collections::FxHashSet;
+use collections::{FxHashMap, FxHashSet};
 use derive_more::{Deref, DerefMut};
 use futures::channel::oneshot;
+use futures::{future::Shared, FutureExt};
+#[cfg(target_os = "macos")]
+use media::core_video::CVImageBuffer;
 use parking_lot::RwLock;
 use refineable::Refineable;
 use slotmap::SlotMap;
 use smallvec::SmallVec;
 use std::{
     any::{Any, TypeId},
-    borrow::{Borrow, BorrowMut},
+    borrow::{Borrow, BorrowMut, Cow},
     cell::{Cell, RefCell},
+    cmp,
     fmt::{Debug, Display},
     future::Future,
     hash::{Hash, Hasher},
     marker::PhantomData,
     mem,
+    ops::Range,
     rc::Rc,
     sync::{
         atomic::{AtomicUsize, Ordering::SeqCst},
@@ -34,12 +44,11 @@ use std::{
     },
     time::{Duration, Instant},
 };
+use util::post_inc;
 use util::{measure, ResultExt};
 
-mod element_cx;
 mod prompts;
 
-pub use element_cx::*;
 pub use prompts::*;
 
 /// Represents the two different phases when dispatching events.
@@ -269,6 +278,192 @@ pub struct DismissEvent;
 
 type FrameCallback = Box<dyn FnOnce(&mut WindowContext)>;
 
+pub(crate) type AnyMouseListener =
+    Box<dyn FnMut(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
+
+#[derive(Clone)]
+pub(crate) struct CursorStyleRequest {
+    pub(crate) hitbox_id: HitboxId,
+    pub(crate) style: CursorStyle,
+}
+
+/// An identifier for a [Hitbox].
+#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
+pub struct HitboxId(usize);
+
+impl HitboxId {
+    /// Checks if the hitbox with this id is currently hovered.
+    pub fn is_hovered(&self, cx: &WindowContext) -> bool {
+        cx.window.mouse_hit_test.0.contains(self)
+    }
+}
+
+/// A rectangular region that potentially blocks hitboxes inserted prior.
+/// See [WindowContext::insert_hitbox] for more details.
+#[derive(Clone, Debug, Deref)]
+pub struct Hitbox {
+    /// A unique identifier for the hitbox.
+    pub id: HitboxId,
+    /// The bounds of the hitbox.
+    #[deref]
+    pub bounds: Bounds<Pixels>,
+    /// The content mask when the hitbox was inserted.
+    pub content_mask: ContentMask<Pixels>,
+    /// Whether the hitbox occludes other hitboxes inserted prior.
+    pub opaque: bool,
+}
+
+impl Hitbox {
+    /// Checks if the hitbox is currently hovered.
+    pub fn is_hovered(&self, cx: &WindowContext) -> bool {
+        self.id.is_hovered(cx)
+    }
+}
+
+#[derive(Default, Eq, PartialEq)]
+pub(crate) struct HitTest(SmallVec<[HitboxId; 8]>);
+
+/// An identifier for a tooltip.
+#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
+pub struct TooltipId(usize);
+
+impl TooltipId {
+    /// Checks if the tooltip is currently hovered.
+    pub fn is_hovered(&self, cx: &WindowContext) -> bool {
+        cx.window
+            .tooltip_bounds
+            .as_ref()
+            .map_or(false, |tooltip_bounds| {
+                tooltip_bounds.id == *self && tooltip_bounds.bounds.contains(&cx.mouse_position())
+            })
+    }
+}
+
+pub(crate) struct TooltipBounds {
+    id: TooltipId,
+    bounds: Bounds<Pixels>,
+}
+
+#[derive(Clone)]
+pub(crate) struct TooltipRequest {
+    id: TooltipId,
+    tooltip: AnyTooltip,
+}
+
+pub(crate) struct DeferredDraw {
+    priority: usize,
+    parent_node: DispatchNodeId,
+    element_id_stack: GlobalElementId,
+    text_style_stack: Vec<TextStyleRefinement>,
+    element: Option<AnyElement>,
+    absolute_offset: Point<Pixels>,
+    prepaint_range: Range<PrepaintStateIndex>,
+    paint_range: Range<PaintIndex>,
+}
+
+pub(crate) struct Frame {
+    pub(crate) focus: Option<FocusId>,
+    pub(crate) window_active: bool,
+    pub(crate) element_states: FxHashMap<(GlobalElementId, TypeId), ElementStateBox>,
+    accessed_element_states: Vec<(GlobalElementId, TypeId)>,
+    pub(crate) mouse_listeners: Vec<Option<AnyMouseListener>>,
+    pub(crate) dispatch_tree: DispatchTree,
+    pub(crate) scene: Scene,
+    pub(crate) hitboxes: Vec<Hitbox>,
+    pub(crate) deferred_draws: Vec<DeferredDraw>,
+    pub(crate) input_handlers: Vec<Option<PlatformInputHandler>>,
+    pub(crate) tooltip_requests: Vec<Option<TooltipRequest>>,
+    pub(crate) cursor_styles: Vec<CursorStyleRequest>,
+    #[cfg(any(test, feature = "test-support"))]
+    pub(crate) debug_bounds: FxHashMap<String, Bounds<Pixels>>,
+}
+
+#[derive(Clone, Default)]
+pub(crate) struct PrepaintStateIndex {
+    hitboxes_index: usize,
+    tooltips_index: usize,
+    deferred_draws_index: usize,
+    dispatch_tree_index: usize,
+    accessed_element_states_index: usize,
+    line_layout_index: LineLayoutIndex,
+}
+
+#[derive(Clone, Default)]
+pub(crate) struct PaintIndex {
+    scene_index: usize,
+    mouse_listeners_index: usize,
+    input_handlers_index: usize,
+    cursor_styles_index: usize,
+    accessed_element_states_index: usize,
+    line_layout_index: LineLayoutIndex,
+}
+
+impl Frame {
+    pub(crate) fn new(dispatch_tree: DispatchTree) -> Self {
+        Frame {
+            focus: None,
+            window_active: false,
+            element_states: FxHashMap::default(),
+            accessed_element_states: Vec::new(),
+            mouse_listeners: Vec::new(),
+            dispatch_tree,
+            scene: Scene::default(),
+            hitboxes: Vec::new(),
+            deferred_draws: Vec::new(),
+            input_handlers: Vec::new(),
+            tooltip_requests: Vec::new(),
+            cursor_styles: Vec::new(),
+
+            #[cfg(any(test, feature = "test-support"))]
+            debug_bounds: FxHashMap::default(),
+        }
+    }
+
+    pub(crate) fn clear(&mut self) {
+        self.element_states.clear();
+        self.accessed_element_states.clear();
+        self.mouse_listeners.clear();
+        self.dispatch_tree.clear();
+        self.scene.clear();
+        self.input_handlers.clear();
+        self.tooltip_requests.clear();
+        self.cursor_styles.clear();
+        self.hitboxes.clear();
+        self.deferred_draws.clear();
+    }
+
+    pub(crate) fn hit_test(&self, position: Point<Pixels>) -> HitTest {
+        let mut hit_test = HitTest::default();
+        for hitbox in self.hitboxes.iter().rev() {
+            let bounds = hitbox.bounds.intersect(&hitbox.content_mask.bounds);
+            if bounds.contains(&position) {
+                hit_test.0.push(hitbox.id);
+                if hitbox.opaque {
+                    break;
+                }
+            }
+        }
+        hit_test
+    }
+
+    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) {
+        for element_state_key in &self.accessed_element_states {
+            if let Some(element_state) = prev_frame.element_states.remove(element_state_key) {
+                self.element_states
+                    .insert(element_state_key.clone(), element_state);
+            }
+        }
+
+        self.scene.finish();
+    }
+}
+
 // Holds the state for a specific window.
 #[doc(hidden)]
 pub struct Window {
@@ -321,7 +516,7 @@ pub struct Window {
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
 pub(crate) enum DrawPhase {
     None,
-    Layout,
+    Prepaint,
     Paint,
     Focus,
 }
@@ -1039,7 +1234,7 @@ impl<'a> WindowContext<'a> {
                 .push(Some(input_handler));
         }
 
-        self.with_element_context(|cx| cx.draw_roots());
+        self.draw_roots();
         self.window.dirty_views.clear();
 
         self.window
@@ -1123,6 +1318,1431 @@ impl<'a> WindowContext<'a> {
         profiling::finish_frame!();
     }
 
+    fn draw_roots(&mut self) {
+        self.window.draw_phase = DrawPhase::Prepaint;
+        self.window.tooltip_bounds.take();
+
+        // Layout all root elements.
+        let mut root_element = self.window.root_view.as_ref().unwrap().clone().into_any();
+        root_element.prepaint_as_root(Point::default(), self.window.viewport_size.into(), self);
+
+        let mut sorted_deferred_draws =
+            (0..self.window.next_frame.deferred_draws.len()).collect::<SmallVec<[_; 8]>>();
+        sorted_deferred_draws.sort_by_key(|ix| self.window.next_frame.deferred_draws[*ix].priority);
+        self.prepaint_deferred_draws(&sorted_deferred_draws);
+
+        let mut prompt_element = None;
+        let mut active_drag_element = None;
+        let mut tooltip_element = None;
+        if let Some(prompt) = self.window.prompt.take() {
+            let mut element = prompt.view.any_view().into_any();
+            element.prepaint_as_root(Point::default(), self.window.viewport_size.into(), self);
+            prompt_element = Some(element);
+            self.window.prompt = Some(prompt);
+        } else if let Some(active_drag) = self.app.active_drag.take() {
+            let mut element = active_drag.view.clone().into_any();
+            let offset = self.mouse_position() - active_drag.cursor_offset;
+            element.prepaint_as_root(offset, AvailableSpace::min_size(), self);
+            active_drag_element = Some(element);
+            self.app.active_drag = Some(active_drag);
+        } else {
+            tooltip_element = self.prepaint_tooltip();
+        }
+
+        self.window.mouse_hit_test = self.window.next_frame.hit_test(self.window.mouse_position);
+
+        // Now actually paint the elements.
+        self.window.draw_phase = DrawPhase::Paint;
+        root_element.paint(self);
+
+        self.paint_deferred_draws(&sorted_deferred_draws);
+
+        if let Some(mut prompt_element) = prompt_element {
+            prompt_element.paint(self)
+        } else if let Some(mut drag_element) = active_drag_element {
+            drag_element.paint(self);
+        } else if let Some(mut tooltip_element) = tooltip_element {
+            tooltip_element.paint(self);
+        }
+    }
+
+    fn prepaint_tooltip(&mut self) -> Option<AnyElement> {
+        let tooltip_request = self.window.next_frame.tooltip_requests.last().cloned()?;
+        let tooltip_request = tooltip_request.unwrap();
+        let mut element = tooltip_request.tooltip.view.clone().into_any();
+        let mouse_position = tooltip_request.tooltip.mouse_position;
+        let tooltip_size = element.layout_as_root(AvailableSpace::min_size(), self);
+
+        let mut tooltip_bounds = Bounds::new(mouse_position + point(px(1.), px(1.)), tooltip_size);
+        let window_bounds = Bounds {
+            origin: Point::default(),
+            size: self.viewport_size(),
+        };
+
+        if tooltip_bounds.right() > window_bounds.right() {
+            let new_x = mouse_position.x - tooltip_bounds.size.width - px(1.);
+            if new_x >= Pixels::ZERO {
+                tooltip_bounds.origin.x = new_x;
+            } else {
+                tooltip_bounds.origin.x = cmp::max(
+                    Pixels::ZERO,
+                    tooltip_bounds.origin.x - tooltip_bounds.right() - window_bounds.right(),
+                );
+            }
+        }
+
+        if tooltip_bounds.bottom() > window_bounds.bottom() {
+            let new_y = mouse_position.y - tooltip_bounds.size.height - px(1.);
+            if new_y >= Pixels::ZERO {
+                tooltip_bounds.origin.y = new_y;
+            } else {
+                tooltip_bounds.origin.y = cmp::max(
+                    Pixels::ZERO,
+                    tooltip_bounds.origin.y - tooltip_bounds.bottom() - window_bounds.bottom(),
+                );
+            }
+        }
+
+        self.with_absolute_element_offset(tooltip_bounds.origin, |cx| element.prepaint(cx));
+
+        self.window.tooltip_bounds = Some(TooltipBounds {
+            id: tooltip_request.id,
+            bounds: tooltip_bounds,
+        });
+        Some(element)
+    }
+
+    fn prepaint_deferred_draws(&mut self, deferred_draw_indices: &[usize]) {
+        assert_eq!(self.window.element_id_stack.len(), 0);
+
+        let mut deferred_draws = mem::take(&mut self.window.next_frame.deferred_draws);
+        for deferred_draw_ix in deferred_draw_indices {
+            let deferred_draw = &mut deferred_draws[*deferred_draw_ix];
+            self.window.element_id_stack = deferred_draw.element_id_stack.clone();
+            self.window.text_style_stack = deferred_draw.text_style_stack.clone();
+            self.window
+                .next_frame
+                .dispatch_tree
+                .set_active_node(deferred_draw.parent_node);
+
+            let prepaint_start = self.prepaint_index();
+            if let Some(element) = deferred_draw.element.as_mut() {
+                self.with_absolute_element_offset(deferred_draw.absolute_offset, |cx| {
+                    element.prepaint(cx)
+                });
+            } else {
+                self.reuse_prepaint(deferred_draw.prepaint_range.clone());
+            }
+            let prepaint_end = self.prepaint_index();
+            deferred_draw.prepaint_range = prepaint_start..prepaint_end;
+        }
+        assert_eq!(
+            self.window.next_frame.deferred_draws.len(),
+            0,
+            "cannot call defer_draw during deferred drawing"
+        );
+        self.window.next_frame.deferred_draws = deferred_draws;
+        self.window.element_id_stack.clear();
+        self.window.text_style_stack.clear();
+    }
+
+    fn paint_deferred_draws(&mut self, deferred_draw_indices: &[usize]) {
+        assert_eq!(self.window.element_id_stack.len(), 0);
+
+        let mut deferred_draws = mem::take(&mut self.window.next_frame.deferred_draws);
+        for deferred_draw_ix in deferred_draw_indices {
+            let mut deferred_draw = &mut deferred_draws[*deferred_draw_ix];
+            self.window.element_id_stack = deferred_draw.element_id_stack.clone();
+            self.window
+                .next_frame
+                .dispatch_tree
+                .set_active_node(deferred_draw.parent_node);
+
+            let paint_start = self.paint_index();
+            if let Some(element) = deferred_draw.element.as_mut() {
+                element.paint(self);
+            } else {
+                self.reuse_paint(deferred_draw.paint_range.clone());
+            }
+            let paint_end = self.paint_index();
+            deferred_draw.paint_range = paint_start..paint_end;
+        }
+        self.window.next_frame.deferred_draws = deferred_draws;
+        self.window.element_id_stack.clear();
+    }
+
+    pub(crate) fn prepaint_index(&self) -> PrepaintStateIndex {
+        PrepaintStateIndex {
+            hitboxes_index: self.window.next_frame.hitboxes.len(),
+            tooltips_index: self.window.next_frame.tooltip_requests.len(),
+            deferred_draws_index: self.window.next_frame.deferred_draws.len(),
+            dispatch_tree_index: self.window.next_frame.dispatch_tree.len(),
+            accessed_element_states_index: self.window.next_frame.accessed_element_states.len(),
+            line_layout_index: self.window.text_system.layout_index(),
+        }
+    }
+
+    pub(crate) fn reuse_prepaint(&mut self, range: Range<PrepaintStateIndex>) {
+        let window = &mut self.window;
+        window.next_frame.hitboxes.extend(
+            window.rendered_frame.hitboxes[range.start.hitboxes_index..range.end.hitboxes_index]
+                .iter()
+                .cloned(),
+        );
+        window.next_frame.tooltip_requests.extend(
+            window.rendered_frame.tooltip_requests
+                [range.start.tooltips_index..range.end.tooltips_index]
+                .iter_mut()
+                .map(|request| request.take()),
+        );
+        window.next_frame.accessed_element_states.extend(
+            window.rendered_frame.accessed_element_states[range.start.accessed_element_states_index
+                ..range.end.accessed_element_states_index]
+                .iter()
+                .cloned(),
+        );
+        window
+            .text_system
+            .reuse_layouts(range.start.line_layout_index..range.end.line_layout_index);
+
+        let reused_subtree = window.next_frame.dispatch_tree.reuse_subtree(
+            range.start.dispatch_tree_index..range.end.dispatch_tree_index,
+            &mut window.rendered_frame.dispatch_tree,
+        );
+        window.next_frame.deferred_draws.extend(
+            window.rendered_frame.deferred_draws
+                [range.start.deferred_draws_index..range.end.deferred_draws_index]
+                .iter()
+                .map(|deferred_draw| DeferredDraw {
+                    parent_node: reused_subtree.refresh_node_id(deferred_draw.parent_node),
+                    element_id_stack: deferred_draw.element_id_stack.clone(),
+                    text_style_stack: deferred_draw.text_style_stack.clone(),
+                    priority: deferred_draw.priority,
+                    element: None,
+                    absolute_offset: deferred_draw.absolute_offset,
+                    prepaint_range: deferred_draw.prepaint_range.clone(),
+                    paint_range: deferred_draw.paint_range.clone(),
+                }),
+        );
+    }
+
+    pub(crate) fn paint_index(&self) -> PaintIndex {
+        PaintIndex {
+            scene_index: self.window.next_frame.scene.len(),
+            mouse_listeners_index: self.window.next_frame.mouse_listeners.len(),
+            input_handlers_index: self.window.next_frame.input_handlers.len(),
+            cursor_styles_index: self.window.next_frame.cursor_styles.len(),
+            accessed_element_states_index: self.window.next_frame.accessed_element_states.len(),
+            line_layout_index: self.window.text_system.layout_index(),
+        }
+    }
+
+    pub(crate) fn reuse_paint(&mut self, range: Range<PaintIndex>) {
+        let window = &mut self.window;
+
+        window.next_frame.cursor_styles.extend(
+            window.rendered_frame.cursor_styles
+                [range.start.cursor_styles_index..range.end.cursor_styles_index]
+                .iter()
+                .cloned(),
+        );
+        window.next_frame.input_handlers.extend(
+            window.rendered_frame.input_handlers
+                [range.start.input_handlers_index..range.end.input_handlers_index]
+                .iter_mut()
+                .map(|handler| handler.take()),
+        );
+        window.next_frame.mouse_listeners.extend(
+            window.rendered_frame.mouse_listeners
+                [range.start.mouse_listeners_index..range.end.mouse_listeners_index]
+                .iter_mut()
+                .map(|listener| listener.take()),
+        );
+        window.next_frame.accessed_element_states.extend(
+            window.rendered_frame.accessed_element_states[range.start.accessed_element_states_index
+                ..range.end.accessed_element_states_index]
+                .iter()
+                .cloned(),
+        );
+        window
+            .text_system
+            .reuse_layouts(range.start.line_layout_index..range.end.line_layout_index);
+        window.next_frame.scene.replay(
+            range.start.scene_index..range.end.scene_index,
+            &window.rendered_frame.scene,
+        );
+    }
+
+    /// Push a text style onto the stack, and call a function with that style active.
+    /// Use [`AppContext::text_style`] to get the current, combined text style. This method
+    /// should only be called as part of element drawing.
+    pub fn with_text_style<F, R>(&mut self, style: Option<TextStyleRefinement>, f: F) -> R
+    where
+        F: FnOnce(&mut Self) -> R,
+    {
+        debug_assert!(
+            matches!(
+                self.window.draw_phase,
+                DrawPhase::Prepaint | DrawPhase::Paint
+            ),
+            "this method can only be called during request_layout, prepaint, or paint"
+        );
+        if let Some(style) = style {
+            self.window.text_style_stack.push(style);
+            let result = f(self);
+            self.window.text_style_stack.pop();
+            result
+        } else {
+            f(self)
+        }
+    }
+
+    /// Updates the cursor style at the platform level. This method should only be called
+    /// during the prepaint phase of element drawing.
+    pub fn set_cursor_style(&mut self, style: CursorStyle, hitbox: &Hitbox) {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Paint,
+            "this method can only be called during paint"
+        );
+        self.window
+            .next_frame
+            .cursor_styles
+            .push(CursorStyleRequest {
+                hitbox_id: hitbox.id,
+                style,
+            });
+    }
+
+    /// Sets a tooltip to be rendered for the upcoming frame. This method should only be called
+    /// during the paint phase of element drawing.
+    pub fn set_tooltip(&mut self, tooltip: AnyTooltip) -> TooltipId {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Prepaint,
+            "this method can only be called during prepaint"
+        );
+        let id = TooltipId(post_inc(&mut self.window.next_tooltip_id.0));
+        self.window
+            .next_frame
+            .tooltip_requests
+            .push(Some(TooltipRequest { id, tooltip }));
+        id
+    }
+
+    /// 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. This method should
+    /// only be called as part of element drawing.
+    pub fn with_element_id<R>(
+        &mut self,
+        id: Option<impl Into<ElementId>>,
+        f: impl FnOnce(&mut Self) -> R,
+    ) -> R {
+        debug_assert!(
+            matches!(
+                self.window.draw_phase,
+                DrawPhase::Prepaint | DrawPhase::Paint
+            ),
+            "this method can only be called during request_layout, prepaint, or paint"
+        );
+        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. This method should only be called during element drawing.
+    pub fn with_content_mask<R>(
+        &mut self,
+        mask: Option<ContentMask<Pixels>>,
+        f: impl FnOnce(&mut Self) -> R,
+    ) -> R {
+        debug_assert!(
+            matches!(
+                self.window.draw_phase,
+                DrawPhase::Prepaint | DrawPhase::Paint
+            ),
+            "this method can only be called during request_layout, prepaint, or paint"
+        );
+        if let Some(mask) = mask {
+            let mask = mask.intersect(&self.content_mask());
+            self.window_mut().content_mask_stack.push(mask);
+            let result = f(self);
+            self.window_mut().content_mask_stack.pop();
+            result
+        } else {
+            f(self)
+        }
+    }
+
+    /// Updates the global element offset relative to the current offset. This is used to implement
+    /// scrolling. This method should only be called during the prepaint phase of element drawing.
+    pub fn with_element_offset<R>(
+        &mut self,
+        offset: Point<Pixels>,
+        f: impl FnOnce(&mut Self) -> R,
+    ) -> R {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Prepaint,
+            "this method can only be called during request_layout, or prepaint"
+        );
+
+        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. This method should only be called during
+    /// the prepaint phase of element drawing.
+    pub fn with_absolute_element_offset<R>(
+        &mut self,
+        offset: Point<Pixels>,
+        f: impl FnOnce(&mut Self) -> R,
+    ) -> R {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Prepaint,
+            "this method can only be called during request_layout, or prepaint"
+        );
+        self.window_mut().element_offset_stack.push(offset);
+        let result = f(self);
+        self.window_mut().element_offset_stack.pop();
+        result
+    }
+
+    /// Perform prepaint on child elements in a "retryable" manner, so that any side effects
+    /// of prepaints can be discarded before prepainting again. This is used to support autoscroll
+    /// where we need to prepaint children to detect the autoscroll bounds, then adjust the
+    /// element offset and prepaint again. See [`List`] for an example. This method should only be
+    /// called during the prepaint phase of element drawing.
+    pub fn transact<T, U>(&mut self, f: impl FnOnce(&mut Self) -> Result<T, U>) -> Result<T, U> {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Prepaint,
+            "this method can only be called during prepaint"
+        );
+        let index = self.prepaint_index();
+        let result = f(self);
+        if result.is_err() {
+            self.window
+                .next_frame
+                .hitboxes
+                .truncate(index.hitboxes_index);
+            self.window
+                .next_frame
+                .tooltip_requests
+                .truncate(index.tooltips_index);
+            self.window
+                .next_frame
+                .deferred_draws
+                .truncate(index.deferred_draws_index);
+            self.window
+                .next_frame
+                .dispatch_tree
+                .truncate(index.dispatch_tree_index);
+            self.window
+                .next_frame
+                .accessed_element_states
+                .truncate(index.accessed_element_states_index);
+            self.window
+                .text_system
+                .truncate_layouts(index.line_layout_index);
+        }
+        result
+    }
+
+    /// When you call this method during [`prepaint`], containing elements will attempt to
+    /// scroll to cause the specified bounds to become visible. When they decide to autoscroll, they will call
+    /// [`prepaint`] again with a new set of bounds. See [`List`] for an example of an element
+    /// that supports this method being called on the elements it contains. This method should only be
+    /// called during the prepaint phase of element drawing.
+    pub fn request_autoscroll(&mut self, bounds: Bounds<Pixels>) {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Prepaint,
+            "this method can only be called during prepaint"
+        );
+        self.window.requested_autoscroll = Some(bounds);
+    }
+
+    /// This method can be called from a containing element such as [`List`] to support the autoscroll behavior
+    /// described in [`request_autoscroll`].
+    pub fn take_autoscroll(&mut self) -> Option<Bounds<Pixels>> {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Prepaint,
+            "this method can only be called during prepaint"
+        );
+        self.window.requested_autoscroll.take()
+    }
+
+    /// Remove an asset from GPUI's cache
+    pub fn remove_cached_asset<A: Asset + 'static>(
+        &mut self,
+        source: &A::Source,
+    ) -> Option<A::Output> {
+        self.asset_cache.remove::<A>(source)
+    }
+
+    /// Asynchronously load an asset, if the asset hasn't finished loading this will return None.
+    /// Your view will be re-drawn once the asset has finished loading.
+    ///
+    /// Note that the multiple calls to this method will only result in one `Asset::load` call.
+    /// The results of that call will be cached, and returned on subsequent uses of this API.
+    ///
+    /// Use [Self::remove_cached_asset] to reload your asset.
+    pub fn use_cached_asset<A: Asset + 'static>(
+        &mut self,
+        source: &A::Source,
+    ) -> Option<A::Output> {
+        self.asset_cache.get::<A>(source).or_else(|| {
+            if let Some(asset) = self.use_asset::<A>(source) {
+                self.asset_cache
+                    .insert::<A>(source.to_owned(), asset.clone());
+                Some(asset)
+            } else {
+                None
+            }
+        })
+    }
+
+    /// Asynchronously load an asset, if the asset hasn't finished loading this will return None.
+    /// Your view will be re-drawn once the asset has finished loading.
+    ///
+    /// Note that the multiple calls to this method will only result in one `Asset::load` call at a
+    /// time.
+    ///
+    /// This asset will not be cached by default, see [Self::use_cached_asset]
+    pub fn use_asset<A: Asset + 'static>(&mut self, source: &A::Source) -> Option<A::Output> {
+        let asset_id = (TypeId::of::<A>(), hash(source));
+        let mut is_first = false;
+        let task = self
+            .loading_assets
+            .remove(&asset_id)
+            .map(|boxed_task| *boxed_task.downcast::<Shared<Task<A::Output>>>().unwrap())
+            .unwrap_or_else(|| {
+                is_first = true;
+                let future = A::load(source.clone(), self);
+                let task = self.background_executor().spawn(future).shared();
+                task
+            });
+
+        task.clone().now_or_never().or_else(|| {
+            if is_first {
+                let parent_id = self.parent_view_id();
+                self.spawn({
+                    let task = task.clone();
+                    |mut cx| async move {
+                        task.await;
+
+                        cx.on_next_frame(move |cx| {
+                            if let Some(parent_id) = parent_id {
+                                cx.notify(parent_id)
+                            } else {
+                                cx.refresh()
+                            }
+                        });
+                    }
+                })
+                .detach();
+            }
+
+            self.loading_assets.insert(asset_id, Box::new(task));
+
+            None
+        })
+    }
+
+    /// Obtain the current element offset. This method should only be called during the
+    /// prepaint phase of element drawing.
+    pub fn element_offset(&self) -> Point<Pixels> {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Prepaint,
+            "this method can only be called during prepaint"
+        );
+        self.window()
+            .element_offset_stack
+            .last()
+            .copied()
+            .unwrap_or_default()
+    }
+
+    /// Obtain the current content mask. This method should only be called during element drawing.
+    pub fn content_mask(&self) -> ContentMask<Pixels> {
+        debug_assert!(
+            matches!(
+                self.window.draw_phase,
+                DrawPhase::Prepaint | DrawPhase::Paint
+            ),
+            "this method can only be called during prepaint, or paint"
+        );
+        self.window()
+            .content_mask_stack
+            .last()
+            .cloned()
+            .unwrap_or_else(|| ContentMask {
+                bounds: Bounds {
+                    origin: Point::default(),
+                    size: self.window().viewport_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. This method should only be called as part of element drawing.
+    pub fn with_element_state<S, R>(
+        &mut self,
+        element_id: Option<ElementId>,
+        f: impl FnOnce(Option<Option<S>>, &mut Self) -> (R, Option<S>),
+    ) -> R
+    where
+        S: 'static,
+    {
+        debug_assert!(
+            matches!(
+                self.window.draw_phase,
+                DrawPhase::Prepaint | DrawPhase::Paint
+            ),
+            "this method can only be called during request_layout, prepaint, or paint"
+        );
+        let id_is_none = element_id.is_none();
+        self.with_element_id(element_id, |cx| {
+                if id_is_none {
+                    let (result, state) = f(None, cx);
+                    debug_assert!(state.is_none(), "you must not return an element state when passing None for the element id");
+                    result
+                } else {
+                    let global_id = cx.window().element_id_stack.clone();
+                    let key = (global_id, TypeId::of::<S>());
+                    cx.window.next_frame.accessed_element_states.push(key.clone());
+
+                    if let Some(any) = cx
+                        .window_mut()
+                        .next_frame
+                        .element_states
+                        .remove(&key)
+                        .or_else(|| {
+                            cx.window_mut()
+                                .rendered_frame
+                                .element_states
+                                .remove(&key)
+                        })
+                    {
+                        let ElementStateBox {
+                            inner,
+                            #[cfg(debug_assertions)]
+                            type_name
+                        } = any;
+                        // Using the extra inner option to avoid needing to reallocate a new box.
+                        let mut state_box = inner
+                            .downcast::<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("reentrant call to with_element_state for the same state type and element id");
+                        let (result, state) = f(Some(Some(state)), cx);
+                        state_box.replace(state.expect("you must return "));
+                        cx.window_mut()
+                            .next_frame
+                            .element_states
+                            .insert(key, ElementStateBox {
+                                inner: state_box,
+                                #[cfg(debug_assertions)]
+                                type_name
+                            });
+                        result
+                    } else {
+                        let (result, state) = f(Some(None), cx);
+                        cx.window_mut()
+                            .next_frame
+                            .element_states
+                            .insert(key,
+                                ElementStateBox {
+                                    inner: Box::new(Some(state.expect("you must return Some<State> when you pass some element id"))),
+                                    #[cfg(debug_assertions)]
+                                    type_name: std::any::type_name::<S>()
+                                }
+
+                            );
+                        result
+                    }
+                }
+            })
+    }
+
+    /// Defers the drawing of the given element, scheduling it to be painted on top of the currently-drawn tree
+    /// at a later time. The `priority` parameter determines the drawing order relative to other deferred elements,
+    /// with higher values being drawn on top.
+    ///
+    /// This method should only be called as part of the prepaint phase of element drawing.
+    pub fn defer_draw(
+        &mut self,
+        element: AnyElement,
+        absolute_offset: Point<Pixels>,
+        priority: usize,
+    ) {
+        let window = &mut self.window;
+        debug_assert_eq!(
+            window.draw_phase,
+            DrawPhase::Prepaint,
+            "this method can only be called during request_layout or prepaint"
+        );
+        let parent_node = window.next_frame.dispatch_tree.active_node_id().unwrap();
+        window.next_frame.deferred_draws.push(DeferredDraw {
+            parent_node,
+            element_id_stack: window.element_id_stack.clone(),
+            text_style_stack: window.text_style_stack.clone(),
+            priority,
+            element: Some(element),
+            absolute_offset,
+            prepaint_range: PrepaintStateIndex::default()..PrepaintStateIndex::default(),
+            paint_range: PaintIndex::default()..PaintIndex::default(),
+        });
+    }
+
+    /// Creates a new painting layer for the specified bounds. A "layer" is a batch
+    /// of geometry that are non-overlapping and have the same draw order. This is typically used
+    /// for performance reasons.
+    ///
+    /// This method should only be called as part of the paint phase of element drawing.
+    pub fn paint_layer<R>(&mut self, bounds: Bounds<Pixels>, f: impl FnOnce(&mut Self) -> R) -> R {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Paint,
+            "this method can only be called during paint"
+        );
+
+        let scale_factor = self.scale_factor();
+        let content_mask = self.content_mask();
+        let clipped_bounds = bounds.intersect(&content_mask.bounds);
+        if !clipped_bounds.is_empty() {
+            self.window
+                .next_frame
+                .scene
+                .push_layer(clipped_bounds.scale(scale_factor));
+        }
+
+        let result = f(self);
+
+        if !clipped_bounds.is_empty() {
+            self.window.next_frame.scene.pop_layer();
+        }
+
+        result
+    }
+
+    /// Paint one or more drop shadows into the scene for the next frame at the current z-index.
+    ///
+    /// This method should only be called as part of the paint phase of element drawing.
+    pub fn paint_shadows(
+        &mut self,
+        bounds: Bounds<Pixels>,
+        corner_radii: Corners<Pixels>,
+        shadows: &[BoxShadow],
+    ) {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Paint,
+            "this method can only be called during paint"
+        );
+
+        let scale_factor = self.scale_factor();
+        let content_mask = self.content_mask();
+        for shadow in shadows {
+            let mut shadow_bounds = bounds;
+            shadow_bounds.origin += shadow.offset;
+            shadow_bounds.dilate(shadow.spread_radius);
+            self.window.next_frame.scene.insert_primitive(Shadow {
+                order: 0,
+                blur_radius: shadow.blur_radius.scale(scale_factor),
+                bounds: shadow_bounds.scale(scale_factor),
+                content_mask: content_mask.scale(scale_factor),
+                corner_radii: corner_radii.scale(scale_factor),
+                color: shadow.color,
+            });
+        }
+    }
+
+    /// 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`](crate::fill), [`outline`](crate::outline), and [`quad`](crate::quad) to construct this type.
+    ///
+    /// This method should only be called as part of the paint phase of element drawing.
+    pub fn paint_quad(&mut self, quad: PaintQuad) {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Paint,
+            "this method can only be called during paint"
+        );
+
+        let scale_factor = self.scale_factor();
+        let content_mask = self.content_mask();
+        self.window.next_frame.scene.insert_primitive(Quad {
+            order: 0,
+            pad: 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.
+    ///
+    /// This method should only be called as part of the paint phase of element drawing.
+    pub fn paint_path(&mut self, mut path: Path<Pixels>, color: impl Into<Hsla>) {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Paint,
+            "this method can only be called during paint"
+        );
+
+        let scale_factor = self.scale_factor();
+        let content_mask = self.content_mask();
+        path.content_mask = content_mask;
+        path.color = color.into();
+        self.window
+            .next_frame
+            .scene
+            .insert_primitive(path.scale(scale_factor));
+    }
+
+    /// Paint an underline into the scene for the next frame at the current z-index.
+    ///
+    /// This method should only be called as part of the paint phase of element drawing.
+    pub fn paint_underline(
+        &mut self,
+        origin: Point<Pixels>,
+        width: Pixels,
+        style: &UnderlineStyle,
+    ) {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Paint,
+            "this method can only be called during paint"
+        );
+
+        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();
+
+        self.window.next_frame.scene.insert_primitive(Underline {
+            order: 0,
+            pad: 0,
+            bounds: bounds.scale(scale_factor),
+            content_mask: content_mask.scale(scale_factor),
+            color: style.color.unwrap_or_default(),
+            thickness: style.thickness.scale(scale_factor),
+            wavy: style.wavy,
+        });
+    }
+
+    /// Paint a strikethrough into the scene for the next frame at the current z-index.
+    ///
+    /// This method should only be called as part of the paint phase of element drawing.
+    pub fn paint_strikethrough(
+        &mut self,
+        origin: Point<Pixels>,
+        width: Pixels,
+        style: &StrikethroughStyle,
+    ) {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Paint,
+            "this method can only be called during paint"
+        );
+
+        let scale_factor = self.scale_factor();
+        let height = style.thickness;
+        let bounds = Bounds {
+            origin,
+            size: size(width, height),
+        };
+        let content_mask = self.content_mask();
+
+        self.window.next_frame.scene.insert_primitive(Underline {
+            order: 0,
+            pad: 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: false,
+        });
+    }
+
+    /// Paints 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.
+    /// You should generally prefer to use the [`ShapedLine::paint`](crate::ShapedLine::paint) or
+    /// [`WrappedLine::paint`](crate::WrappedLine::paint) methods in the [`TextSystem`](crate::TextSystem).
+    /// This method is only useful if you need to paint a single glyph that has already been shaped.
+    ///
+    /// This method should only be called as part of the paint phase of element drawing.
+    pub fn paint_glyph(
+        &mut self,
+        origin: Point<Pixels>,
+        font_id: FontId,
+        glyph_id: GlyphId,
+        font_size: Pixels,
+        color: Hsla,
+    ) -> Result<()> {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Paint,
+            "this method can only be called during paint"
+        );
+
+        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);
+            self.window
+                .next_frame
+                .scene
+                .insert_primitive(MonochromeSprite {
+                    order: 0,
+                    pad: 0,
+                    bounds,
+                    content_mask,
+                    color,
+                    tile,
+                    transformation: TransformationMatrix::unit(),
+                });
+        }
+        Ok(())
+    }
+
+    /// Paints 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.
+    /// You should generally prefer to use the [`ShapedLine::paint`](crate::ShapedLine::paint) or
+    /// [`WrappedLine::paint`](crate::WrappedLine::paint) methods in the [`TextSystem`](crate::TextSystem).
+    /// This method is only useful if you need to paint a single emoji that has already been shaped.
+    ///
+    /// This method should only be called as part of the paint phase of element drawing.
+    pub fn paint_emoji(
+        &mut self,
+        origin: Point<Pixels>,
+        font_id: FontId,
+        glyph_id: GlyphId,
+        font_size: Pixels,
+    ) -> Result<()> {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Paint,
+            "this method can only be called during paint"
+        );
+
+        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);
+
+            self.window
+                .next_frame
+                .scene
+                .insert_primitive(PolychromeSprite {
+                    order: 0,
+                    grayscale: false,
+                    bounds,
+                    corner_radii: Default::default(),
+                    content_mask,
+                    tile,
+                });
+        }
+        Ok(())
+    }
+
+    /// Paint a monochrome SVG into the scene for the next frame at the current stacking context.
+    ///
+    /// This method should only be called as part of the paint phase of element drawing.
+    pub fn paint_svg(
+        &mut self,
+        bounds: Bounds<Pixels>,
+        path: SharedString,
+        transformation: TransformationMatrix,
+        color: Hsla,
+    ) -> Result<()> {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Paint,
+            "this method can only be called during paint"
+        );
+
+        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);
+
+        self.window
+            .next_frame
+            .scene
+            .insert_primitive(MonochromeSprite {
+                order: 0,
+                pad: 0,
+                bounds,
+                content_mask,
+                color,
+                tile,
+                transformation,
+            });
+
+        Ok(())
+    }
+
+    /// Paint an image into the scene for the next frame at the current z-index.
+    ///
+    /// This method should only be called as part of the paint phase of element drawing.
+    pub fn paint_image(
+        &mut self,
+        bounds: Bounds<Pixels>,
+        corner_radii: Corners<Pixels>,
+        data: Arc<ImageData>,
+        grayscale: bool,
+    ) -> Result<()> {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Paint,
+            "this method can only be called during paint"
+        );
+
+        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);
+
+        self.window
+            .next_frame
+            .scene
+            .insert_primitive(PolychromeSprite {
+                order: 0,
+                grayscale,
+                bounds,
+                content_mask,
+                corner_radii,
+                tile,
+            });
+        Ok(())
+    }
+
+    /// Paint a surface into the scene for the next frame at the current z-index.
+    ///
+    /// This method should only be called as part of the paint phase of element drawing.
+    #[cfg(target_os = "macos")]
+    pub fn paint_surface(&mut self, bounds: Bounds<Pixels>, image_buffer: CVImageBuffer) {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Paint,
+            "this method can only be called during paint"
+        );
+
+        let scale_factor = self.scale_factor();
+        let bounds = bounds.scale(scale_factor);
+        let content_mask = self.content_mask().scale(scale_factor);
+        self.window
+            .next_frame
+            .scene
+            .insert_primitive(crate::Surface {
+                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::request_layout`] trait method and enables any element to participate in layout.
+    ///
+    /// This method should only be called as part of the request_layout or prepaint phase of element drawing.
+    pub fn request_layout(
+        &mut self,
+        style: &Style,
+        children: impl IntoIterator<Item = LayoutId>,
+    ) -> LayoutId {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Prepaint,
+            "this method can only be called during request_layout, or prepaint"
+        );
+
+        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`.
+    ///
+    /// This method should only be called as part of the request_layout or prepaint phase of element drawing.
+    pub fn request_measured_layout<
+        F: FnMut(Size<Option<Pixels>>, Size<AvailableSpace>, &mut WindowContext) -> Size<Pixels>
+            + 'static,
+    >(
+        &mut self,
+        style: Style,
+        measure: F,
+    ) -> LayoutId {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Prepaint,
+            "this method can only be called during request_layout, or prepaint"
+        );
+
+        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.
+    ///
+    /// This method should only be called as part of the prepaint phase of element drawing.
+    pub fn compute_layout(&mut self, layout_id: LayoutId, available_space: Size<AvailableSpace>) {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Prepaint,
+            "this method can only be called during request_layout, or prepaint"
+        );
+
+        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 will usually be invoked by
+    /// GPUI itself automatically in order to pass your element its `Bounds` automatically.
+    ///
+    /// This method should only be called as part of element drawing.
+    pub fn layout_bounds(&mut self, layout_id: LayoutId) -> Bounds<Pixels> {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Prepaint,
+            "this method can only be called during request_layout, prepaint, or paint"
+        );
+
+        let mut bounds = self
+            .window
+            .layout_engine
+            .as_mut()
+            .unwrap()
+            .layout_bounds(layout_id)
+            .map(Into::into);
+        bounds.origin += self.element_offset();
+        bounds
+    }
+
+    /// This method should be called during `prepaint`. You can use
+    /// the returned [Hitbox] during `paint` or in an event handler
+    /// to determine whether the inserted hitbox was the topmost.
+    ///
+    /// This method should only be called as part of the prepaint phase of element drawing.
+    pub fn insert_hitbox(&mut self, bounds: Bounds<Pixels>, opaque: bool) -> Hitbox {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Prepaint,
+            "this method can only be called during prepaint"
+        );
+
+        let content_mask = self.content_mask();
+        let window = &mut self.window;
+        let id = window.next_hitbox_id;
+        window.next_hitbox_id.0 += 1;
+        let hitbox = Hitbox {
+            id,
+            bounds,
+            content_mask,
+            opaque,
+        };
+        window.next_frame.hitboxes.push(hitbox.clone());
+        hitbox
+    }
+
+    /// Sets the key context for the current element. This context will be used to translate
+    /// keybindings into actions.
+    ///
+    /// This method should only be called as part of the paint phase of element drawing.
+    pub fn set_key_context(&mut self, context: KeyContext) {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Paint,
+            "this method can only be called during paint"
+        );
+        self.window
+            .next_frame
+            .dispatch_tree
+            .set_key_context(context);
+    }
+
+    /// Sets the focus handle for the current element. This handle will be used to manage focus state
+    /// and keyboard event dispatch for the element.
+    ///
+    /// This method should only be called as part of the paint phase of element drawing.
+    pub fn set_focus_handle(&mut self, focus_handle: &FocusHandle) {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Paint,
+            "this method can only be called during paint"
+        );
+        self.window
+            .next_frame
+            .dispatch_tree
+            .set_focus_id(focus_handle.id);
+    }
+
+    /// Sets the view id for the current element, which will be used to manage view caching.
+    ///
+    /// This method should only be called as part of element prepaint. We plan on removing this
+    /// method eventually when we solve some issues that require us to construct editor elements
+    /// directly instead of always using editors via views.
+    pub fn set_view_id(&mut self, view_id: EntityId) {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Prepaint,
+            "this method can only be called during prepaint"
+        );
+        self.window.next_frame.dispatch_tree.set_view_id(view_id);
+    }
+
+    /// Get the last view id for the current element
+    pub fn parent_view_id(&mut self) -> Option<EntityId> {
+        self.window.next_frame.dispatch_tree.parent_view_id()
+    }
+
+    /// 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.
+    ///
+    /// This method should only be called as part of the paint phase of element drawing.
+    ///
+    /// [element_input_handler]: crate::ElementInputHandler
+    pub fn handle_input(&mut self, focus_handle: &FocusHandle, input_handler: impl InputHandler) {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Paint,
+            "this method can only be called during paint"
+        );
+
+        if focus_handle.is_focused(self) {
+            let cx = self.to_async();
+            self.window
+                .next_frame
+                .input_handlers
+                .push(Some(PlatformInputHandler::new(cx, 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.
+    ///
+    /// This method should only be called as part of the paint phase of element drawing.
+    pub fn on_mouse_event<Event: MouseEvent>(
+        &mut self,
+        mut handler: impl FnMut(&Event, DispatchPhase, &mut WindowContext) + 'static,
+    ) {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Paint,
+            "this method can only be called during paint"
+        );
+
+        self.window.next_frame.mouse_listeners.push(Some(Box::new(
+            move |event: &dyn Any, phase: DispatchPhase, cx: &mut WindowContext<'_>| {
+                if let Some(event) = event.downcast_ref() {
+                    handler(event, 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.
+    ///
+    /// This method should only be called as part of the paint phase of element drawing.
+    pub fn on_key_event<Event: KeyEvent>(
+        &mut self,
+        listener: impl Fn(&Event, DispatchPhase, &mut WindowContext) + 'static,
+    ) {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Paint,
+            "this method can only be called during paint"
+        );
+
+        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 a modifiers changed event listener on the window for the next frame.
+    ///
+    /// 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.
+    ///
+    /// This method should only be called as part of the paint phase of element drawing.
+    pub fn on_modifiers_changed(
+        &mut self,
+        listener: impl Fn(&ModifiersChangedEvent, &mut WindowContext) + 'static,
+    ) {
+        debug_assert_eq!(
+            self.window.draw_phase,
+            DrawPhase::Paint,
+            "this method can only be called during paint"
+        );
+
+        self.window
+            .next_frame
+            .dispatch_tree
+            .on_modifiers_changed(Rc::new(
+                move |event: &ModifiersChangedEvent, cx: &mut WindowContext<'_>| {
+                    listener(event, cx)
+                },
+            ));
+    }
+
     fn reset_cursor_style(&self) {
         // Set the cursor only if we're the active window.
         if self.is_window_active() {

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

@@ -1,1554 +0,0 @@
-//! The element context is the main interface for interacting with the frame during a paint.
-//!
-//! Elements are hierarchical and with a few exceptions the context accumulates state in a stack
-//! as it processes all of the elements in the frame. The methods that interact with this stack
-//! are generally marked with `with_*`, and take a callback to denote the region of code that
-//! should be executed with that state.
-//!
-//! The other main interface is the `paint_*` family of methods, which push basic drawing commands
-//! to the GPU. Everything in a GPUI app is drawn with these methods.
-//!
-//! There are also several internal methods that GPUI uses, such as [`ElementContext::with_element_state`]
-//! to call the paint and layout methods on elements. These have been included as they're often useful
-//! for taking manual control of the layouting or painting of specialized elements.
-
-use std::{
-    any::{Any, TypeId},
-    borrow::{Borrow, BorrowMut, Cow},
-    cmp, mem,
-    ops::Range,
-    rc::Rc,
-    sync::Arc,
-};
-
-use anyhow::Result;
-use collections::FxHashMap;
-use derive_more::{Deref, DerefMut};
-use futures::{future::Shared, FutureExt};
-#[cfg(target_os = "macos")]
-use media::core_video::CVImageBuffer;
-use smallvec::SmallVec;
-use util::post_inc;
-
-use crate::{
-    hash, point, prelude::*, px, size, AnyElement, AnyTooltip, AppContext, Asset, AvailableSpace,
-    Bounds, BoxShadow, ContentMask, Corners, CursorStyle, DevicePixels, DispatchNodeId,
-    DispatchPhase, DispatchTree, DrawPhase, ElementId, ElementStateBox, EntityId, FocusHandle,
-    FocusId, FontId, GlobalElementId, GlyphId, Hsla, ImageData, InputHandler, IsZero, KeyContext,
-    KeyEvent, LayoutId, LineLayoutIndex, ModifiersChangedEvent, MonochromeSprite, MouseEvent,
-    PaintQuad, Path, Pixels, PlatformInputHandler, Point, PolychromeSprite, Quad,
-    RenderGlyphParams, RenderImageParams, RenderSvgParams, Scene, Shadow, SharedString, Size,
-    StrikethroughStyle, Style, Task, TextStyleRefinement, TransformationMatrix, Underline,
-    UnderlineStyle, Window, WindowContext, SUBPIXEL_VARIANTS,
-};
-
-pub(crate) type AnyMouseListener =
-    Box<dyn FnMut(&dyn Any, DispatchPhase, &mut ElementContext) + 'static>;
-
-#[derive(Clone)]
-pub(crate) struct CursorStyleRequest {
-    pub(crate) hitbox_id: HitboxId,
-    pub(crate) style: CursorStyle,
-}
-
-/// An identifier for a [Hitbox].
-#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
-pub struct HitboxId(usize);
-
-impl HitboxId {
-    /// Checks if the hitbox with this id is currently hovered.
-    pub fn is_hovered(&self, cx: &WindowContext) -> bool {
-        cx.window.mouse_hit_test.0.contains(self)
-    }
-}
-
-/// A rectangular region that potentially blocks hitboxes inserted prior.
-/// See [ElementContext::insert_hitbox] for more details.
-#[derive(Clone, Debug, Deref)]
-pub struct Hitbox {
-    /// A unique identifier for the hitbox.
-    pub id: HitboxId,
-    /// The bounds of the hitbox.
-    #[deref]
-    pub bounds: Bounds<Pixels>,
-    /// The content mask when the hitbox was inserted.
-    pub content_mask: ContentMask<Pixels>,
-    /// Whether the hitbox occludes other hitboxes inserted prior.
-    pub opaque: bool,
-}
-
-impl Hitbox {
-    /// Checks if the hitbox is currently hovered.
-    pub fn is_hovered(&self, cx: &WindowContext) -> bool {
-        self.id.is_hovered(cx)
-    }
-}
-
-#[derive(Default, Eq, PartialEq)]
-pub(crate) struct HitTest(SmallVec<[HitboxId; 8]>);
-
-/// An identifier for a tooltip.
-#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
-pub struct TooltipId(usize);
-
-impl TooltipId {
-    /// Checks if the tooltip is currently hovered.
-    pub fn is_hovered(&self, cx: &WindowContext) -> bool {
-        cx.window
-            .tooltip_bounds
-            .as_ref()
-            .map_or(false, |tooltip_bounds| {
-                tooltip_bounds.id == *self && tooltip_bounds.bounds.contains(&cx.mouse_position())
-            })
-    }
-}
-
-pub(crate) struct TooltipBounds {
-    id: TooltipId,
-    bounds: Bounds<Pixels>,
-}
-
-#[derive(Clone)]
-pub(crate) struct TooltipRequest {
-    id: TooltipId,
-    tooltip: AnyTooltip,
-}
-
-pub(crate) struct DeferredDraw {
-    priority: usize,
-    parent_node: DispatchNodeId,
-    element_id_stack: GlobalElementId,
-    text_style_stack: Vec<TextStyleRefinement>,
-    element: Option<AnyElement>,
-    absolute_offset: Point<Pixels>,
-    prepaint_range: Range<PrepaintStateIndex>,
-    paint_range: Range<PaintIndex>,
-}
-
-pub(crate) struct Frame {
-    pub(crate) focus: Option<FocusId>,
-    pub(crate) window_active: bool,
-    pub(crate) element_states: FxHashMap<(GlobalElementId, TypeId), ElementStateBox>,
-    accessed_element_states: Vec<(GlobalElementId, TypeId)>,
-    pub(crate) mouse_listeners: Vec<Option<AnyMouseListener>>,
-    pub(crate) dispatch_tree: DispatchTree,
-    pub(crate) scene: Scene,
-    pub(crate) hitboxes: Vec<Hitbox>,
-    pub(crate) deferred_draws: Vec<DeferredDraw>,
-    pub(crate) input_handlers: Vec<Option<PlatformInputHandler>>,
-    pub(crate) tooltip_requests: Vec<Option<TooltipRequest>>,
-    pub(crate) cursor_styles: Vec<CursorStyleRequest>,
-    #[cfg(any(test, feature = "test-support"))]
-    pub(crate) debug_bounds: FxHashMap<String, Bounds<Pixels>>,
-}
-
-#[derive(Clone, Default)]
-pub(crate) struct PrepaintStateIndex {
-    hitboxes_index: usize,
-    tooltips_index: usize,
-    deferred_draws_index: usize,
-    dispatch_tree_index: usize,
-    accessed_element_states_index: usize,
-    line_layout_index: LineLayoutIndex,
-}
-
-#[derive(Clone, Default)]
-pub(crate) struct PaintIndex {
-    scene_index: usize,
-    mouse_listeners_index: usize,
-    input_handlers_index: usize,
-    cursor_styles_index: usize,
-    accessed_element_states_index: usize,
-    line_layout_index: LineLayoutIndex,
-}
-
-impl Frame {
-    pub(crate) fn new(dispatch_tree: DispatchTree) -> Self {
-        Frame {
-            focus: None,
-            window_active: false,
-            element_states: FxHashMap::default(),
-            accessed_element_states: Vec::new(),
-            mouse_listeners: Vec::new(),
-            dispatch_tree,
-            scene: Scene::default(),
-            hitboxes: Vec::new(),
-            deferred_draws: Vec::new(),
-            input_handlers: Vec::new(),
-            tooltip_requests: Vec::new(),
-            cursor_styles: Vec::new(),
-
-            #[cfg(any(test, feature = "test-support"))]
-            debug_bounds: FxHashMap::default(),
-        }
-    }
-
-    pub(crate) fn clear(&mut self) {
-        self.element_states.clear();
-        self.accessed_element_states.clear();
-        self.mouse_listeners.clear();
-        self.dispatch_tree.clear();
-        self.scene.clear();
-        self.input_handlers.clear();
-        self.tooltip_requests.clear();
-        self.cursor_styles.clear();
-        self.hitboxes.clear();
-        self.deferred_draws.clear();
-    }
-
-    pub(crate) fn hit_test(&self, position: Point<Pixels>) -> HitTest {
-        let mut hit_test = HitTest::default();
-        for hitbox in self.hitboxes.iter().rev() {
-            let bounds = hitbox.bounds.intersect(&hitbox.content_mask.bounds);
-            if bounds.contains(&position) {
-                hit_test.0.push(hitbox.id);
-                if hitbox.opaque {
-                    break;
-                }
-            }
-        }
-        hit_test
-    }
-
-    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) {
-        for element_state_key in &self.accessed_element_states {
-            if let Some(element_state) = prev_frame.element_states.remove(element_state_key) {
-                self.element_states
-                    .insert(element_state_key.clone(), element_state);
-            }
-        }
-
-        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> {
-    /// Convert this window context into an ElementContext in this callback.
-    /// If you need to use this method, you're probably intermixing the imperative
-    /// and declarative APIs, which is not recommended.
-    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 reserve_model<T: 'static>(&mut self) -> Self::Result<crate::Reservation<T>> {
-        self.cx.reserve_model()
-    }
-
-    fn insert_model<T: 'static>(
-        &mut self,
-        reservation: crate::Reservation<T>,
-        build_model: impl FnOnce(&mut crate::ModelContext<'_, T>) -> T,
-    ) -> Self::Result<crate::Model<T>> {
-        self.cx.insert_model(reservation, 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 draw_roots(&mut self) {
-        self.window.draw_phase = DrawPhase::Layout;
-        self.window.tooltip_bounds.take();
-
-        // Layout all root elements.
-        let mut root_element = self.window.root_view.as_ref().unwrap().clone().into_any();
-        root_element.prepaint_as_root(Point::default(), self.window.viewport_size.into(), self);
-
-        let mut sorted_deferred_draws =
-            (0..self.window.next_frame.deferred_draws.len()).collect::<SmallVec<[_; 8]>>();
-        sorted_deferred_draws.sort_by_key(|ix| self.window.next_frame.deferred_draws[*ix].priority);
-        self.prepaint_deferred_draws(&sorted_deferred_draws);
-
-        let mut prompt_element = None;
-        let mut active_drag_element = None;
-        let mut tooltip_element = None;
-        if let Some(prompt) = self.window.prompt.take() {
-            let mut element = prompt.view.any_view().into_any();
-            element.prepaint_as_root(Point::default(), self.window.viewport_size.into(), self);
-            prompt_element = Some(element);
-            self.window.prompt = Some(prompt);
-        } else if let Some(active_drag) = self.app.active_drag.take() {
-            let mut element = active_drag.view.clone().into_any();
-            let offset = self.mouse_position() - active_drag.cursor_offset;
-            element.prepaint_as_root(offset, AvailableSpace::min_size(), self);
-            active_drag_element = Some(element);
-            self.app.active_drag = Some(active_drag);
-        } else {
-            tooltip_element = self.prepaint_tooltip();
-        }
-
-        self.window.mouse_hit_test = self.window.next_frame.hit_test(self.window.mouse_position);
-
-        // Now actually paint the elements.
-        self.window.draw_phase = DrawPhase::Paint;
-        root_element.paint(self);
-
-        self.paint_deferred_draws(&sorted_deferred_draws);
-
-        if let Some(mut prompt_element) = prompt_element {
-            prompt_element.paint(self)
-        } else if let Some(mut drag_element) = active_drag_element {
-            drag_element.paint(self);
-        } else if let Some(mut tooltip_element) = tooltip_element {
-            tooltip_element.paint(self);
-        }
-    }
-
-    fn prepaint_tooltip(&mut self) -> Option<AnyElement> {
-        let tooltip_request = self.window.next_frame.tooltip_requests.last().cloned()?;
-        let tooltip_request = tooltip_request.unwrap();
-        let mut element = tooltip_request.tooltip.view.clone().into_any();
-        let mouse_position = tooltip_request.tooltip.mouse_position;
-        let tooltip_size = element.layout_as_root(AvailableSpace::min_size(), self);
-
-        let mut tooltip_bounds = Bounds::new(mouse_position + point(px(1.), px(1.)), tooltip_size);
-        let window_bounds = Bounds {
-            origin: Point::default(),
-            size: self.viewport_size(),
-        };
-
-        if tooltip_bounds.right() > window_bounds.right() {
-            let new_x = mouse_position.x - tooltip_bounds.size.width - px(1.);
-            if new_x >= Pixels::ZERO {
-                tooltip_bounds.origin.x = new_x;
-            } else {
-                tooltip_bounds.origin.x = cmp::max(
-                    Pixels::ZERO,
-                    tooltip_bounds.origin.x - tooltip_bounds.right() - window_bounds.right(),
-                );
-            }
-        }
-
-        if tooltip_bounds.bottom() > window_bounds.bottom() {
-            let new_y = mouse_position.y - tooltip_bounds.size.height - px(1.);
-            if new_y >= Pixels::ZERO {
-                tooltip_bounds.origin.y = new_y;
-            } else {
-                tooltip_bounds.origin.y = cmp::max(
-                    Pixels::ZERO,
-                    tooltip_bounds.origin.y - tooltip_bounds.bottom() - window_bounds.bottom(),
-                );
-            }
-        }
-
-        self.with_absolute_element_offset(tooltip_bounds.origin, |cx| element.prepaint(cx));
-
-        self.window.tooltip_bounds = Some(TooltipBounds {
-            id: tooltip_request.id,
-            bounds: tooltip_bounds,
-        });
-        Some(element)
-    }
-
-    fn prepaint_deferred_draws(&mut self, deferred_draw_indices: &[usize]) {
-        assert_eq!(self.window.element_id_stack.len(), 0);
-
-        let mut deferred_draws = mem::take(&mut self.window.next_frame.deferred_draws);
-        for deferred_draw_ix in deferred_draw_indices {
-            let deferred_draw = &mut deferred_draws[*deferred_draw_ix];
-            self.window.element_id_stack = deferred_draw.element_id_stack.clone();
-            self.window.text_style_stack = deferred_draw.text_style_stack.clone();
-            self.window
-                .next_frame
-                .dispatch_tree
-                .set_active_node(deferred_draw.parent_node);
-
-            let prepaint_start = self.prepaint_index();
-            if let Some(element) = deferred_draw.element.as_mut() {
-                self.with_absolute_element_offset(deferred_draw.absolute_offset, |cx| {
-                    element.prepaint(cx)
-                });
-            } else {
-                self.reuse_prepaint(deferred_draw.prepaint_range.clone());
-            }
-            let prepaint_end = self.prepaint_index();
-            deferred_draw.prepaint_range = prepaint_start..prepaint_end;
-        }
-        assert_eq!(
-            self.window.next_frame.deferred_draws.len(),
-            0,
-            "cannot call defer_draw during deferred drawing"
-        );
-        self.window.next_frame.deferred_draws = deferred_draws;
-        self.window.element_id_stack.clear();
-        self.window.text_style_stack.clear();
-    }
-
-    fn paint_deferred_draws(&mut self, deferred_draw_indices: &[usize]) {
-        assert_eq!(self.window.element_id_stack.len(), 0);
-
-        let mut deferred_draws = mem::take(&mut self.window.next_frame.deferred_draws);
-        for deferred_draw_ix in deferred_draw_indices {
-            let mut deferred_draw = &mut deferred_draws[*deferred_draw_ix];
-            self.window.element_id_stack = deferred_draw.element_id_stack.clone();
-            self.window
-                .next_frame
-                .dispatch_tree
-                .set_active_node(deferred_draw.parent_node);
-
-            let paint_start = self.paint_index();
-            if let Some(element) = deferred_draw.element.as_mut() {
-                element.paint(self);
-            } else {
-                self.reuse_paint(deferred_draw.paint_range.clone());
-            }
-            let paint_end = self.paint_index();
-            deferred_draw.paint_range = paint_start..paint_end;
-        }
-        self.window.next_frame.deferred_draws = deferred_draws;
-        self.window.element_id_stack.clear();
-    }
-
-    pub(crate) fn prepaint_index(&self) -> PrepaintStateIndex {
-        PrepaintStateIndex {
-            hitboxes_index: self.window.next_frame.hitboxes.len(),
-            tooltips_index: self.window.next_frame.tooltip_requests.len(),
-            deferred_draws_index: self.window.next_frame.deferred_draws.len(),
-            dispatch_tree_index: self.window.next_frame.dispatch_tree.len(),
-            accessed_element_states_index: self.window.next_frame.accessed_element_states.len(),
-            line_layout_index: self.window.text_system.layout_index(),
-        }
-    }
-
-    pub(crate) fn reuse_prepaint(&mut self, range: Range<PrepaintStateIndex>) {
-        let window = &mut self.window;
-        window.next_frame.hitboxes.extend(
-            window.rendered_frame.hitboxes[range.start.hitboxes_index..range.end.hitboxes_index]
-                .iter()
-                .cloned(),
-        );
-        window.next_frame.tooltip_requests.extend(
-            window.rendered_frame.tooltip_requests
-                [range.start.tooltips_index..range.end.tooltips_index]
-                .iter_mut()
-                .map(|request| request.take()),
-        );
-        window.next_frame.accessed_element_states.extend(
-            window.rendered_frame.accessed_element_states[range.start.accessed_element_states_index
-                ..range.end.accessed_element_states_index]
-                .iter()
-                .cloned(),
-        );
-        window
-            .text_system
-            .reuse_layouts(range.start.line_layout_index..range.end.line_layout_index);
-
-        let reused_subtree = window.next_frame.dispatch_tree.reuse_subtree(
-            range.start.dispatch_tree_index..range.end.dispatch_tree_index,
-            &mut window.rendered_frame.dispatch_tree,
-        );
-        window.next_frame.deferred_draws.extend(
-            window.rendered_frame.deferred_draws
-                [range.start.deferred_draws_index..range.end.deferred_draws_index]
-                .iter()
-                .map(|deferred_draw| DeferredDraw {
-                    parent_node: reused_subtree.refresh_node_id(deferred_draw.parent_node),
-                    element_id_stack: deferred_draw.element_id_stack.clone(),
-                    text_style_stack: deferred_draw.text_style_stack.clone(),
-                    priority: deferred_draw.priority,
-                    element: None,
-                    absolute_offset: deferred_draw.absolute_offset,
-                    prepaint_range: deferred_draw.prepaint_range.clone(),
-                    paint_range: deferred_draw.paint_range.clone(),
-                }),
-        );
-    }
-
-    pub(crate) fn paint_index(&self) -> PaintIndex {
-        PaintIndex {
-            scene_index: self.window.next_frame.scene.len(),
-            mouse_listeners_index: self.window.next_frame.mouse_listeners.len(),
-            input_handlers_index: self.window.next_frame.input_handlers.len(),
-            cursor_styles_index: self.window.next_frame.cursor_styles.len(),
-            accessed_element_states_index: self.window.next_frame.accessed_element_states.len(),
-            line_layout_index: self.window.text_system.layout_index(),
-        }
-    }
-
-    pub(crate) fn reuse_paint(&mut self, range: Range<PaintIndex>) {
-        let window = &mut self.cx.window;
-
-        window.next_frame.cursor_styles.extend(
-            window.rendered_frame.cursor_styles
-                [range.start.cursor_styles_index..range.end.cursor_styles_index]
-                .iter()
-                .cloned(),
-        );
-        window.next_frame.input_handlers.extend(
-            window.rendered_frame.input_handlers
-                [range.start.input_handlers_index..range.end.input_handlers_index]
-                .iter_mut()
-                .map(|handler| handler.take()),
-        );
-        window.next_frame.mouse_listeners.extend(
-            window.rendered_frame.mouse_listeners
-                [range.start.mouse_listeners_index..range.end.mouse_listeners_index]
-                .iter_mut()
-                .map(|listener| listener.take()),
-        );
-        window.next_frame.accessed_element_states.extend(
-            window.rendered_frame.accessed_element_states[range.start.accessed_element_states_index
-                ..range.end.accessed_element_states_index]
-                .iter()
-                .cloned(),
-        );
-        window
-            .text_system
-            .reuse_layouts(range.start.line_layout_index..range.end.line_layout_index);
-        window.next_frame.scene.replay(
-            range.start.scene_index..range.end.scene_index,
-            &window.rendered_frame.scene,
-        );
-    }
-
-    /// Push a text style onto the stack, and call a function with that style active.
-    /// Use [`AppContext::text_style`] to get the current, combined text 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.window.text_style_stack.push(style);
-            let result = f(self);
-            self.window.text_style_stack.pop();
-            result
-        } else {
-            f(self)
-        }
-    }
-
-    /// Updates the cursor style at the platform level.
-    pub fn set_cursor_style(&mut self, style: CursorStyle, hitbox: &Hitbox) {
-        self.window
-            .next_frame
-            .cursor_styles
-            .push(CursorStyleRequest {
-                hitbox_id: hitbox.id,
-                style,
-            });
-    }
-
-    /// Sets a tooltip to be rendered for the upcoming frame
-    pub fn set_tooltip(&mut self, tooltip: AnyTooltip) -> TooltipId {
-        let id = TooltipId(post_inc(&mut self.window.next_tooltip_id.0));
-        self.window
-            .next_frame
-            .tooltip_requests
-            .push(Some(TooltipRequest { id, tooltip }));
-        id
-    }
-
-    /// 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().content_mask_stack.push(mask);
-            let result = f(self);
-            self.window_mut().content_mask_stack.pop();
-            result
-        } else {
-            f(self)
-        }
-    }
-
-    /// 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().element_offset_stack.push(offset);
-        let result = f(self);
-        self.window_mut().element_offset_stack.pop();
-        result
-    }
-
-    /// Perform prepaint on child elements in a "retryable" manner, so that any side effects
-    /// of prepaints can be discarded before prepainting again. This is used to support autoscroll
-    /// where we need to prepaint children to detect the autoscroll bounds, then adjust the
-    /// element offset and prepaint again. See [`List`] for an example.
-    pub fn transact<T, U>(&mut self, f: impl FnOnce(&mut Self) -> Result<T, U>) -> Result<T, U> {
-        let index = self.prepaint_index();
-        let result = f(self);
-        if result.is_err() {
-            self.window
-                .next_frame
-                .hitboxes
-                .truncate(index.hitboxes_index);
-            self.window
-                .next_frame
-                .tooltip_requests
-                .truncate(index.tooltips_index);
-            self.window
-                .next_frame
-                .deferred_draws
-                .truncate(index.deferred_draws_index);
-            self.window
-                .next_frame
-                .dispatch_tree
-                .truncate(index.dispatch_tree_index);
-            self.window
-                .next_frame
-                .accessed_element_states
-                .truncate(index.accessed_element_states_index);
-            self.window
-                .text_system
-                .truncate_layouts(index.line_layout_index);
-        }
-        result
-    }
-
-    /// When you call this method during [`prepaint`], containing elements will attempt to
-    /// scroll to cause the specified bounds to become visible. When they decide to autoscroll, they will call
-    /// [`prepaint`] again with a new set of bounds. See [`List`] for an example of an element
-    /// that supports this method being called on the elements it contains.
-    pub fn request_autoscroll(&mut self, bounds: Bounds<Pixels>) {
-        self.window.requested_autoscroll = Some(bounds);
-    }
-
-    /// This method can be called from a containing element such as [`List`] to support the autoscroll behavior
-    /// described in [`request_autoscroll`].
-    pub fn take_autoscroll(&mut self) -> Option<Bounds<Pixels>> {
-        self.window.requested_autoscroll.take()
-    }
-
-    /// Remove an asset from GPUI's cache
-    pub fn remove_cached_asset<A: Asset + 'static>(
-        &mut self,
-        source: &A::Source,
-    ) -> Option<A::Output> {
-        self.asset_cache.remove::<A>(source)
-    }
-
-    /// Asynchronously load an asset, if the asset hasn't finished loading this will return None.
-    /// Your view will be re-drawn once the asset has finished loading.
-    ///
-    /// Note that the multiple calls to this method will only result in one `Asset::load` call.
-    /// The results of that call will be cached, and returned on subsequent uses of this API.
-    ///
-    /// Use [Self::remove_cached_asset] to reload your asset.
-    pub fn use_cached_asset<A: Asset + 'static>(
-        &mut self,
-        source: &A::Source,
-    ) -> Option<A::Output> {
-        self.asset_cache.get::<A>(source).or_else(|| {
-            if let Some(asset) = self.use_asset::<A>(source) {
-                self.asset_cache
-                    .insert::<A>(source.to_owned(), asset.clone());
-                Some(asset)
-            } else {
-                None
-            }
-        })
-    }
-
-    /// Asynchronously load an asset, if the asset hasn't finished loading this will return None.
-    /// Your view will be re-drawn once the asset has finished loading.
-    ///
-    /// Note that the multiple calls to this method will only result in one `Asset::load` call at a
-    /// time.
-    ///
-    /// This asset will not be cached by default, see [Self::use_cached_asset]
-    pub fn use_asset<A: Asset + 'static>(&mut self, source: &A::Source) -> Option<A::Output> {
-        let asset_id = (TypeId::of::<A>(), hash(source));
-        let mut is_first = false;
-        let task = self
-            .loading_assets
-            .remove(&asset_id)
-            .map(|boxed_task| *boxed_task.downcast::<Shared<Task<A::Output>>>().unwrap())
-            .unwrap_or_else(|| {
-                is_first = true;
-                let future = A::load(source.clone(), self);
-                let task = self.background_executor().spawn(future).shared();
-                task
-            });
-
-        task.clone().now_or_never().or_else(|| {
-            if is_first {
-                let parent_id = self.parent_view_id();
-                self.spawn({
-                    let task = task.clone();
-                    |mut cx| async move {
-                        task.await;
-
-                        cx.on_next_frame(move |cx| {
-                            if let Some(parent_id) = parent_id {
-                                cx.notify(parent_id)
-                            } else {
-                                cx.refresh()
-                            }
-                        });
-                    }
-                })
-                .detach();
-            }
-
-            self.loading_assets.insert(asset_id, Box::new(task));
-
-            None
-        })
-    }
-
-    /// Obtain the current element offset.
-    pub fn element_offset(&self) -> Point<Pixels> {
-        self.window()
-            .element_offset_stack
-            .last()
-            .copied()
-            .unwrap_or_default()
-    }
-
-    /// Obtain the current content mask.
-    pub fn content_mask(&self) -> ContentMask<Pixels> {
-        self.window()
-            .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,
-        element_id: Option<ElementId>,
-        f: impl FnOnce(Option<Option<S>>, &mut Self) -> (R, Option<S>),
-    ) -> R
-    where
-        S: 'static,
-    {
-        let id_is_none = element_id.is_none();
-        self.with_element_id(element_id, |cx| {
-            if id_is_none {
-                let (result, state) = f(None, cx);
-                debug_assert!(state.is_none(), "you must not return an element state when passing None for the element id");
-                result
-            } else {
-                let global_id = cx.window().element_id_stack.clone();
-                let key = (global_id, TypeId::of::<S>());
-                cx.window.next_frame.accessed_element_states.push(key.clone());
-
-                if let Some(any) = cx
-                    .window_mut()
-                    .next_frame
-                    .element_states
-                    .remove(&key)
-                    .or_else(|| {
-                        cx.window_mut()
-                            .rendered_frame
-                            .element_states
-                            .remove(&key)
-                    })
-                {
-                    let ElementStateBox {
-                        inner,
-                        #[cfg(debug_assertions)]
-                        type_name
-                    } = any;
-                    // Using the extra inner option to avoid needing to reallocate a new box.
-                    let mut state_box = inner
-                        .downcast::<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("reentrant call to with_element_state for the same state type and element id");
-                    let (result, state) = f(Some(Some(state)), cx);
-                    state_box.replace(state.expect("you must return "));
-                    cx.window_mut()
-                        .next_frame
-                        .element_states
-                        .insert(key, ElementStateBox {
-                            inner: state_box,
-                            #[cfg(debug_assertions)]
-                            type_name
-                        });
-                    result
-                } else {
-                    let (result, state) = f(Some(None), cx);
-                    cx.window_mut()
-                        .next_frame
-                        .element_states
-                        .insert(key,
-                            ElementStateBox {
-                                inner: Box::new(Some(state.expect("you must return Some<State> when you pass some element id"))),
-                                #[cfg(debug_assertions)]
-                                type_name: std::any::type_name::<S>()
-                            }
-
-                        );
-                    result
-                }
-            }
-        })
-    }
-
-    /// Defers the drawing of the given element, scheduling it to be painted on top of the currently-drawn tree
-    /// at a later time. The `priority` parameter determines the drawing order relative to other deferred elements,
-    /// with higher values being drawn on top.
-    pub fn defer_draw(
-        &mut self,
-        element: AnyElement,
-        absolute_offset: Point<Pixels>,
-        priority: usize,
-    ) {
-        let window = &mut self.cx.window;
-        assert_eq!(
-            window.draw_phase,
-            DrawPhase::Layout,
-            "defer_draw can only be called during request_layout or prepaint"
-        );
-        let parent_node = window.next_frame.dispatch_tree.active_node_id().unwrap();
-        window.next_frame.deferred_draws.push(DeferredDraw {
-            parent_node,
-            element_id_stack: window.element_id_stack.clone(),
-            text_style_stack: window.text_style_stack.clone(),
-            priority,
-            element: Some(element),
-            absolute_offset,
-            prepaint_range: PrepaintStateIndex::default()..PrepaintStateIndex::default(),
-            paint_range: PaintIndex::default()..PaintIndex::default(),
-        });
-    }
-
-    /// Creates a new painting layer for the specified bounds. A "layer" is a batch
-    /// of geometry that are non-overlapping and have the same draw order. This is typically used
-    /// for performance reasons.
-    pub fn paint_layer<R>(&mut self, bounds: Bounds<Pixels>, f: impl FnOnce(&mut Self) -> R) -> R {
-        let scale_factor = self.scale_factor();
-        let content_mask = self.content_mask();
-        let clipped_bounds = bounds.intersect(&content_mask.bounds);
-        if !clipped_bounds.is_empty() {
-            self.window
-                .next_frame
-                .scene
-                .push_layer(clipped_bounds.scale(scale_factor));
-        }
-
-        let result = f(self);
-
-        if !clipped_bounds.is_empty() {
-            self.window.next_frame.scene.pop_layer();
-        }
-
-        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();
-        for shadow in shadows {
-            let mut shadow_bounds = bounds;
-            shadow_bounds.origin += shadow.offset;
-            shadow_bounds.dilate(shadow.spread_radius);
-            self.window.next_frame.scene.insert_primitive(Shadow {
-                order: 0,
-                blur_radius: shadow.blur_radius.scale(scale_factor),
-                bounds: shadow_bounds.scale(scale_factor),
-                content_mask: content_mask.scale(scale_factor),
-                corner_radii: corner_radii.scale(scale_factor),
-                color: shadow.color,
-            });
-        }
-    }
-
-    /// 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`](crate::fill), [`outline`](crate::outline), and [`quad`](crate::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();
-        self.window.next_frame.scene.insert_primitive(Quad {
-            order: 0,
-            pad: 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();
-        path.content_mask = content_mask;
-        path.color = color.into();
-        self.window
-            .next_frame
-            .scene
-            .insert_primitive(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();
-
-        self.window.next_frame.scene.insert_primitive(Underline {
-            order: 0,
-            pad: 0,
-            bounds: bounds.scale(scale_factor),
-            content_mask: content_mask.scale(scale_factor),
-            color: style.color.unwrap_or_default(),
-            thickness: style.thickness.scale(scale_factor),
-            wavy: style.wavy,
-        });
-    }
-
-    /// Paint a strikethrough into the scene for the next frame at the current z-index.
-    pub fn paint_strikethrough(
-        &mut self,
-        origin: Point<Pixels>,
-        width: Pixels,
-        style: &StrikethroughStyle,
-    ) {
-        let scale_factor = self.scale_factor();
-        let height = style.thickness;
-        let bounds = Bounds {
-            origin,
-            size: size(width, height),
-        };
-        let content_mask = self.content_mask();
-
-        self.window.next_frame.scene.insert_primitive(Underline {
-            order: 0,
-            pad: 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: false,
-        });
-    }
-
-    /// Paints 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.
-    /// You should generally prefer to use the [`ShapedLine::paint`](crate::ShapedLine::paint) or
-    /// [`WrappedLine::paint`](crate::WrappedLine::paint) methods in the [`TextSystem`](crate::TextSystem).
-    /// This method is only useful if you need to paint a single glyph that has already been shaped.
-    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);
-            self.window
-                .next_frame
-                .scene
-                .insert_primitive(MonochromeSprite {
-                    order: 0,
-                    pad: 0,
-                    bounds,
-                    content_mask,
-                    color,
-                    tile,
-                    transformation: TransformationMatrix::unit(),
-                });
-        }
-        Ok(())
-    }
-
-    /// Paints 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.
-    /// You should generally prefer to use the [`ShapedLine::paint`](crate::ShapedLine::paint) or
-    /// [`WrappedLine::paint`](crate::WrappedLine::paint) methods in the [`TextSystem`](crate::TextSystem).
-    /// This method is only useful if you need to paint a single emoji that has already been shaped.
-    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);
-
-            self.window
-                .next_frame
-                .scene
-                .insert_primitive(PolychromeSprite {
-                    order: 0,
-                    grayscale: false,
-                    bounds,
-                    corner_radii: Default::default(),
-                    content_mask,
-                    tile,
-                });
-        }
-        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,
-        transformation: TransformationMatrix,
-        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);
-
-        self.window
-            .next_frame
-            .scene
-            .insert_primitive(MonochromeSprite {
-                order: 0,
-                pad: 0,
-                bounds,
-                content_mask,
-                color,
-                tile,
-                transformation,
-            });
-
-        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);
-
-        self.window
-            .next_frame
-            .scene
-            .insert_primitive(PolychromeSprite {
-                order: 0,
-                grayscale,
-                bounds,
-                content_mask,
-                corner_radii,
-                tile,
-            });
-        Ok(())
-    }
-
-    /// Paint a surface into the scene for the next frame at the current z-index.
-    #[cfg(target_os = "macos")]
-    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);
-        self.window
-            .next_frame
-            .scene
-            .insert_primitive(crate::Surface {
-                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 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
-    }
-
-    /// This method should be called during `prepaint`. You can use
-    /// the returned [Hitbox] during `paint` or in an event handler
-    /// to determine whether the inserted hitbox was the topmost.
-    pub fn insert_hitbox(&mut self, bounds: Bounds<Pixels>, opaque: bool) -> Hitbox {
-        let content_mask = self.content_mask();
-        let window = &mut self.window;
-        let id = window.next_hitbox_id;
-        window.next_hitbox_id.0 += 1;
-        let hitbox = Hitbox {
-            id,
-            bounds,
-            content_mask,
-            opaque,
-        };
-        window.next_frame.hitboxes.push(hitbox.clone());
-        hitbox
-    }
-
-    /// Sets the key context for the current element. This context will be used to translate
-    /// keybindings into actions.
-    pub fn set_key_context(&mut self, context: KeyContext) {
-        self.window
-            .next_frame
-            .dispatch_tree
-            .set_key_context(context);
-    }
-
-    /// Sets the focus handle for the current element. This handle will be used to manage focus state
-    /// and keyboard event dispatch for the element.
-    pub fn set_focus_handle(&mut self, focus_handle: &FocusHandle) {
-        self.window
-            .next_frame
-            .dispatch_tree
-            .set_focus_id(focus_handle.id);
-    }
-
-    /// Sets the view id for the current element, which will be used to manage view caching.
-    pub fn set_view_id(&mut self, view_id: EntityId) {
-        self.window.next_frame.dispatch_tree.set_view_id(view_id);
-    }
-
-    /// Get the last view id for the current element
-    pub fn parent_view_id(&mut self) -> Option<EntityId> {
-        self.window.next_frame.dispatch_tree.parent_view_id()
-    }
-
-    /// 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 cx = self.to_async();
-            self.window
-                .next_frame
-                .input_handlers
-                .push(Some(PlatformInputHandler::new(cx, 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,
-    ) {
-        self.window.next_frame.mouse_listeners.push(Some(Box::new(
-            move |event: &dyn Any, phase: DispatchPhase, cx: &mut ElementContext<'_>| {
-                if let Some(event) = event.downcast_ref() {
-                    handler(event, 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)
-                }
-            },
-        ));
-    }
-
-    /// Register a modifiers changed event listener on the window for the next frame.
-    ///
-    /// 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_modifiers_changed(
-        &mut self,
-        listener: impl Fn(&ModifiersChangedEvent, &mut ElementContext) + 'static,
-    ) {
-        self.window
-            .next_frame
-            .dispatch_tree
-            .on_modifiers_changed(Rc::new(
-                move |event: &ModifiersChangedEvent, cx: &mut ElementContext<'_>| {
-                    listener(event, cx)
-                },
-            ));
-    }
-}

crates/image_viewer/src/image_viewer.rs 🔗

@@ -155,7 +155,7 @@ impl FocusableView for ImageView {
 
 impl Render for ImageView {
     fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        let checkered_background = |bounds: Bounds<Pixels>, _, cx: &mut ElementContext| {
+        let checkered_background = |bounds: Bounds<Pixels>, _, cx: &mut WindowContext| {
             let square_size = 32.0;
 
             let start_y = bounds.origin.y.0;

crates/terminal_view/src/terminal_element.rs 🔗

@@ -1,11 +1,11 @@
 use editor::{CursorLayout, HighlightedRange, HighlightedRangeLine};
 use gpui::{
-    div, fill, point, px, relative, AnyElement, Bounds, DispatchPhase, Element, ElementContext,
-    FocusHandle, Font, FontStyle, FontWeight, HighlightStyle, Hitbox, Hsla, InputHandler,
-    InteractiveElement, Interactivity, IntoElement, LayoutId, Model, ModelContext,
-    ModifiersChangedEvent, MouseButton, MouseMoveEvent, Pixels, Point, ShapedLine,
-    StatefulInteractiveElement, StrikethroughStyle, Styled, TextRun, TextStyle, UnderlineStyle,
-    WeakView, WhiteSpace, WindowContext, WindowTextSystem,
+    div, fill, point, px, relative, AnyElement, Bounds, DispatchPhase, Element, FocusHandle, Font,
+    FontStyle, FontWeight, HighlightStyle, Hitbox, Hsla, InputHandler, InteractiveElement,
+    Interactivity, IntoElement, LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton,
+    MouseMoveEvent, Pixels, Point, ShapedLine, StatefulInteractiveElement, StrikethroughStyle,
+    Styled, TextRun, TextStyle, UnderlineStyle, WeakView, WhiteSpace, WindowContext,
+    WindowTextSystem,
 };
 use itertools::Itertools;
 use language::CursorShape;
@@ -85,7 +85,7 @@ impl LayoutCell {
         origin: Point<Pixels>,
         layout: &LayoutState,
         _visible_bounds: Bounds<Pixels>,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         let pos = {
             let point = self.point;
@@ -124,7 +124,7 @@ impl LayoutRect {
         }
     }
 
-    fn paint(&self, origin: Point<Pixels>, layout: &LayoutState, cx: &mut ElementContext) {
+    fn paint(&self, origin: Point<Pixels>, layout: &LayoutState, cx: &mut WindowContext) {
         let position = {
             let alac_point = self.point;
             point(
@@ -418,7 +418,7 @@ impl TerminalElement {
         origin: Point<Pixels>,
         mode: TermMode,
         hitbox: &Hitbox,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         let focus = self.focus.clone();
         let terminal = self.terminal.clone();
@@ -544,7 +544,7 @@ impl Element for TerminalElement {
     type RequestLayoutState = ();
     type PrepaintState = LayoutState;
 
-    fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+    fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
         self.interactivity.occlude_mouse();
         let layout_id = self.interactivity.request_layout(cx, |mut style, cx| {
             style.size.width = relative(1.).into();
@@ -560,7 +560,7 @@ impl Element for TerminalElement {
         &mut self,
         bounds: Bounds<Pixels>,
         _: &mut Self::RequestLayoutState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Self::PrepaintState {
         self.interactivity
             .prepaint(bounds, bounds.size, cx, |_, _, hitbox, cx| {
@@ -777,7 +777,7 @@ impl Element for TerminalElement {
         bounds: Bounds<Pixels>,
         _: &mut Self::RequestLayoutState,
         layout: &mut Self::PrepaintState,
-        cx: &mut ElementContext<'_>,
+        cx: &mut WindowContext<'_>,
     ) {
         cx.paint_quad(fill(bounds, layout.background_color));
         let origin = bounds.origin + Point::new(layout.gutter, px(0.));

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

@@ -2,9 +2,9 @@ use std::{cell::RefCell, rc::Rc};
 
 use gpui::{
     anchored, deferred, div, point, prelude::FluentBuilder, px, AnchorCorner, AnyElement, Bounds,
-    DismissEvent, DispatchPhase, Element, ElementContext, ElementId, HitboxId, InteractiveElement,
-    IntoElement, LayoutId, ManagedView, MouseDownEvent, ParentElement, Pixels, Point, View,
-    VisualContext, WindowContext,
+    DismissEvent, DispatchPhase, Element, ElementId, HitboxId, InteractiveElement, IntoElement,
+    LayoutId, ManagedView, MouseDownEvent, ParentElement, Pixels, Point, View, VisualContext,
+    WindowContext,
 };
 
 use crate::prelude::*;
@@ -112,8 +112,8 @@ impl<M: ManagedView> PopoverMenu<M> {
 
     fn with_element_state<R>(
         &mut self,
-        cx: &mut ElementContext,
-        f: impl FnOnce(&mut Self, &mut PopoverMenuElementState<M>, &mut ElementContext) -> R,
+        cx: &mut WindowContext,
+        f: impl FnOnce(&mut Self, &mut PopoverMenuElementState<M>, &mut WindowContext) -> R,
     ) -> R {
         cx.with_element_state::<PopoverMenuElementState<M>, _>(
             Some(self.id.clone()),
@@ -173,7 +173,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
 
     fn request_layout(
         &mut self,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> (gpui::LayoutId, Self::RequestLayoutState) {
         self.with_element_state(cx, |this, element_state, cx| {
             let mut menu_layout_id = None;
@@ -221,7 +221,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
         &mut self,
         _bounds: Bounds<Pixels>,
         request_layout: &mut Self::RequestLayoutState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> Option<HitboxId> {
         self.with_element_state(cx, |_this, element_state, cx| {
             if let Some(child) = request_layout.child_element.as_mut() {
@@ -245,7 +245,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
         _: Bounds<gpui::Pixels>,
         request_layout: &mut Self::RequestLayoutState,
         child_hitbox: &mut Option<HitboxId>,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         self.with_element_state(cx, |_this, _element_state, cx| {
             if let Some(mut child) = request_layout.child_element.take() {

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

@@ -2,9 +2,8 @@ use std::{cell::RefCell, rc::Rc};
 
 use gpui::{
     anchored, deferred, div, AnchorCorner, AnyElement, Bounds, DismissEvent, DispatchPhase,
-    Element, ElementContext, ElementId, Hitbox, InteractiveElement, IntoElement, LayoutId,
-    ManagedView, MouseButton, MouseDownEvent, ParentElement, Pixels, Point, View, VisualContext,
-    WindowContext,
+    Element, ElementId, Hitbox, InteractiveElement, IntoElement, LayoutId, ManagedView,
+    MouseButton, MouseDownEvent, ParentElement, Pixels, Point, View, VisualContext, WindowContext,
 };
 
 pub struct RightClickMenu<M: ManagedView> {
@@ -41,8 +40,8 @@ impl<M: ManagedView> RightClickMenu<M> {
 
     fn with_element_state<R>(
         &mut self,
-        cx: &mut ElementContext,
-        f: impl FnOnce(&mut Self, &mut MenuHandleElementState<M>, &mut ElementContext) -> R,
+        cx: &mut WindowContext,
+        f: impl FnOnce(&mut Self, &mut MenuHandleElementState<M>, &mut WindowContext) -> R,
     ) -> R {
         cx.with_element_state::<MenuHandleElementState<M>, _>(
             Some(self.id.clone()),
@@ -89,19 +88,24 @@ impl<M> Default for MenuHandleElementState<M> {
     }
 }
 
-pub struct MenuHandleFrameState {
+pub struct RequestLayoutState {
     child_layout_id: Option<LayoutId>,
     child_element: Option<AnyElement>,
     menu_element: Option<AnyElement>,
 }
 
+pub struct PrepaintState {
+    hitbox: Hitbox,
+    child_bounds: Option<Bounds<Pixels>>,
+}
+
 impl<M: ManagedView> Element for RightClickMenu<M> {
-    type RequestLayoutState = MenuHandleFrameState;
-    type PrepaintState = Hitbox;
+    type RequestLayoutState = RequestLayoutState;
+    type PrepaintState = PrepaintState;
 
     fn request_layout(
         &mut self,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) -> (gpui::LayoutId, Self::RequestLayoutState) {
         self.with_element_state(cx, |this, element_state, cx| {
             let mut menu_layout_id = None;
@@ -137,7 +141,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
 
             (
                 layout_id,
-                MenuHandleFrameState {
+                RequestLayoutState {
                     child_element,
                     child_layout_id,
                     menu_element,
@@ -150,8 +154,8 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
         &mut self,
         bounds: Bounds<Pixels>,
         request_layout: &mut Self::RequestLayoutState,
-        cx: &mut ElementContext,
-    ) -> Hitbox {
+        cx: &mut WindowContext,
+    ) -> PrepaintState {
         cx.with_element_id(Some(self.id.clone()), |cx| {
             let hitbox = cx.insert_hitbox(bounds, false);
 
@@ -163,7 +167,12 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
                 menu.prepaint(cx);
             }
 
-            hitbox
+            PrepaintState {
+                hitbox,
+                child_bounds: request_layout
+                    .child_layout_id
+                    .map(|layout_id| cx.layout_bounds(layout_id)),
+            }
         })
     }
 
@@ -171,8 +180,8 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
         &mut self,
         _bounds: Bounds<gpui::Pixels>,
         request_layout: &mut Self::RequestLayoutState,
-        hitbox: &mut Self::PrepaintState,
-        cx: &mut ElementContext,
+        prepaint_state: &mut Self::PrepaintState,
+        cx: &mut WindowContext,
     ) {
         self.with_element_state(cx, |this, element_state, cx| {
             if let Some(mut child) = request_layout.child_element.take() {
@@ -191,10 +200,9 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
             let attach = this.attach;
             let menu = element_state.menu.clone();
             let position = element_state.position.clone();
-            let child_layout_id = request_layout.child_layout_id;
-            let child_bounds = cx.layout_bounds(child_layout_id.unwrap());
+            let child_bounds = prepaint_state.child_bounds;
 
-            let hitbox_id = hitbox.id;
+            let hitbox_id = prepaint_state.hitbox.id;
             cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
                 if phase == DispatchPhase::Bubble
                     && event.button == MouseButton::Right
@@ -219,7 +227,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
                     .detach();
                     cx.focus_view(&new_menu);
                     *menu.borrow_mut() = Some(new_menu);
-                    *position.borrow_mut() = if child_layout_id.is_some() {
+                    *position.borrow_mut() = if let Some(child_bounds) = child_bounds {
                         if let Some(attach) = attach {
                             attach.corner(child_bounds)
                         } else {

crates/ui/src/prelude.rs 🔗

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

crates/workspace/src/pane_group.rs 🔗

@@ -759,7 +759,7 @@ mod element {
         fn layout_handle(
             axis: Axis,
             pane_bounds: Bounds<Pixels>,
-            cx: &mut ElementContext,
+            cx: &mut WindowContext,
         ) -> PaneAxisHandleLayout {
             let handle_bounds = Bounds {
                 origin: pane_bounds.origin.apply_along(axis, |origin| {
@@ -797,7 +797,7 @@ mod element {
 
         fn request_layout(
             &mut self,
-            cx: &mut ui::prelude::ElementContext,
+            cx: &mut ui::prelude::WindowContext,
         ) -> (gpui::LayoutId, Self::RequestLayoutState) {
             let mut style = Style::default();
             style.flex_grow = 1.;
@@ -812,7 +812,7 @@ mod element {
             &mut self,
             bounds: Bounds<Pixels>,
             _state: &mut Self::RequestLayoutState,
-            cx: &mut ElementContext,
+            cx: &mut WindowContext,
         ) -> PaneAxisLayout {
             let dragged_handle = cx.with_element_state::<Rc<RefCell<Option<usize>>>, _>(
                 Some(self.basis.into()),
@@ -900,7 +900,7 @@ mod element {
             bounds: gpui::Bounds<ui::prelude::Pixels>,
             _: &mut Self::RequestLayoutState,
             layout: &mut Self::PrepaintState,
-            cx: &mut ui::prelude::ElementContext,
+            cx: &mut ui::prelude::WindowContext,
         ) {
             for child in &mut layout.children {
                 child.element.paint(cx);

crates/workspace/src/workspace.rs 🔗

@@ -78,9 +78,9 @@ use theme::{ActiveTheme, SystemAppearance, ThemeSettings};
 pub use toolbar::{Toolbar, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
 pub use ui;
 use ui::{
-    div, h_flex, Context as _, Div, Element, ElementContext, FluentBuilder,
-    InteractiveElement as _, IntoElement, Label, ParentElement as _, Pixels, SharedString,
-    Styled as _, ViewContext, VisualContext as _, WindowContext,
+    div, h_flex, Context as _, Div, Element, FluentBuilder, InteractiveElement as _, IntoElement,
+    Label, ParentElement as _, Pixels, SharedString, Styled as _, ViewContext, VisualContext as _,
+    WindowContext,
 };
 use util::{maybe, ResultExt};
 use uuid::Uuid;
@@ -4991,7 +4991,7 @@ impl Element for DisconnectedOverlay {
     type RequestLayoutState = AnyElement;
     type PrepaintState = ();
 
-    fn request_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::RequestLayoutState) {
+    fn request_layout(&mut self, cx: &mut WindowContext) -> (LayoutId, Self::RequestLayoutState) {
         let mut background = cx.theme().colors().elevated_surface_background;
         background.fade_out(0.2);
         let mut overlay = div()
@@ -5016,7 +5016,7 @@ impl Element for DisconnectedOverlay {
         &mut self,
         bounds: Bounds<Pixels>,
         overlay: &mut Self::RequestLayoutState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         cx.insert_hitbox(bounds, true);
         overlay.prepaint(cx);
@@ -5027,7 +5027,7 @@ impl Element for DisconnectedOverlay {
         _: Bounds<Pixels>,
         overlay: &mut Self::RequestLayoutState,
         _: &mut Self::PrepaintState,
-        cx: &mut ElementContext,
+        cx: &mut WindowContext,
     ) {
         overlay.paint(cx)
     }