Remove 2 suffix from gpui

Max Brunsfeld and Mikayla created

Co-authored-by: Mikayla <mikayla@zed.dev>

Change summary

Cargo.lock                                         |  207 
Cargo.toml                                         |    2 
crates/activity_indicator/Cargo.toml               |    2 
crates/ai/Cargo.toml                               |    4 
crates/assistant/Cargo.toml                        |    2 
crates/audio/Cargo.toml                            |    2 
crates/auto_update/Cargo.toml                      |    2 
crates/breadcrumbs/Cargo.toml                      |    4 
crates/call/Cargo.toml                             |    4 
crates/channel/Cargo.toml                          |    4 
crates/client/Cargo.toml                           |    4 
crates/collab/Cargo.toml                           |    2 
crates/collab2/Cargo.toml                          |   99 
crates/collab_ui/Cargo.toml                        |    4 
crates/command_palette/Cargo.toml                  |    4 
crates/copilot/Cargo.toml                          |    4 
crates/copilot_button/Cargo.toml                   |    2 
crates/db/Cargo.toml                               |    4 
crates/diagnostics/Cargo.toml                      |    4 
crates/editor/Cargo.toml                           |    4 
crates/feature_flags/Cargo.toml                    |    2 
crates/feedback/Cargo.toml                         |    2 
crates/file_finder/Cargo.toml                      |    4 
crates/fs/Cargo.toml                               |    4 
crates/fuzzy/Cargo.toml                            |    2 
crates/go_to_line/Cargo.toml                       |    2 
crates/gpui/Cargo.toml                             |   32 
crates/gpui/build.rs                               |   85 
crates/gpui/docs/contexts.md                       |    0 
crates/gpui/docs/key_dispatch.md                   |    0 
crates/gpui/examples/components.rs                 |  237 
crates/gpui/examples/corner_radii.rs               |  154 
crates/gpui/examples/text.rs                       |   81 
crates/gpui/src/action.rs                          |    0 
crates/gpui/src/app.rs                             | 7536 ++-------------
crates/gpui/src/app/action.rs                      |  120 
crates/gpui/src/app/async_context.rs               |    0 
crates/gpui/src/app/callback_collection.rs         |  164 
crates/gpui/src/app/entity_map.rs                  |    0 
crates/gpui/src/app/menu.rs                        |   99 
crates/gpui/src/app/model_context.rs               |    0 
crates/gpui/src/app/ref_counts.rs                  |  220 
crates/gpui/src/app/test_app_context.rs            |  661 -
crates/gpui/src/app/test_context.rs                |    0 
crates/gpui/src/app/window.rs                      | 1767 ---
crates/gpui/src/app/window_input_handler.rs        |   90 
crates/gpui/src/arena.rs                           |    0 
crates/gpui/src/assets.rs                          |   77 
crates/gpui/src/clipboard.rs                       |   42 
crates/gpui/src/color.rs                           |  493 
crates/gpui/src/dispatch.rs                        |    1 
crates/gpui/src/element.rs                         |    0 
crates/gpui/src/elements.rs                        |  740 -
crates/gpui/src/elements/align.rs                  |  115 
crates/gpui/src/elements/canvas.rs                 |  109 
crates/gpui/src/elements/clipped.rs                |   71 
crates/gpui/src/elements/component.rs              |  342 
crates/gpui/src/elements/constrained_box.rs        |  187 
crates/gpui/src/elements/container.rs              |  684 -
crates/gpui/src/elements/div.rs                    |    0 
crates/gpui/src/elements/empty.rs                  |   89 
crates/gpui/src/elements/expanded.rs               |   96 
crates/gpui/src/elements/flex.rs                   |  512 -
crates/gpui/src/elements/hook.rs                   |   85 
crates/gpui/src/elements/image.rs                  |  137 
crates/gpui/src/elements/img.rs                    |    0 
crates/gpui/src/elements/keystroke_label.rs        |  100 
crates/gpui/src/elements/label.rs                  |  280 
crates/gpui/src/elements/list.rs                   | 1086 -
crates/gpui/src/elements/mod.rs                    |    0 
crates/gpui/src/elements/mouse_event_handler.rs    |  323 
crates/gpui/src/elements/overlay.rs                |  401 
crates/gpui/src/elements/resizable.rs              |  290 
crates/gpui/src/elements/stack.rs                  |  104 
crates/gpui/src/elements/svg.rs                    |  161 
crates/gpui/src/elements/text.rs                   |  733 
crates/gpui/src/elements/tooltip.rs                |  244 
crates/gpui/src/elements/uniform_list.rs           |  570 
crates/gpui/src/executor.rs                        | 1018 -
crates/gpui/src/font_cache.rs                      |  330 
crates/gpui/src/fonts.rs                           |  636 -
crates/gpui/src/geometry.rs                        | 2637 +++++
crates/gpui/src/gpui.rs                            |  239 
crates/gpui/src/image_cache.rs                     |   33 
crates/gpui/src/image_data.rs                      |   43 
crates/gpui/src/input.rs                           |    0 
crates/gpui/src/interactive.rs                     |    0 
crates/gpui/src/json.rs                            |   15 
crates/gpui/src/key_dispatch.rs                    |    0 
crates/gpui/src/keymap/binding.rs                  |    0 
crates/gpui/src/keymap/context.rs                  |    0 
crates/gpui/src/keymap/keymap.rs                   |    0 
crates/gpui/src/keymap/matcher.rs                  |    0 
crates/gpui/src/keymap/mod.rs                      |    0 
crates/gpui/src/keymap_matcher.rs                  |  587 -
crates/gpui/src/keymap_matcher/binding.rs          |  111 
crates/gpui/src/keymap_matcher/keymap.rs           |  392 
crates/gpui/src/keymap_matcher/keymap_context.rs   |  326 
crates/gpui/src/keymap_matcher/keystroke.rs        |  141 
crates/gpui/src/platform.rs                        |  565 
crates/gpui/src/platform/app_menu.rs               |    0 
crates/gpui/src/platform/event.rs                  |  236 
crates/gpui/src/platform/keystroke.rs              |    0 
crates/gpui/src/platform/mac.rs                    |   93 
crates/gpui/src/platform/mac/appearance.rs         |   36 
crates/gpui/src/platform/mac/atlas.rs              |  173 
crates/gpui/src/platform/mac/dispatcher.rs         |   71 
crates/gpui/src/platform/mac/display.rs            |    0 
crates/gpui/src/platform/mac/display_linker.rs     |    0 
crates/gpui/src/platform/mac/event.rs              |  359 
crates/gpui/src/platform/mac/events.rs             |    0 
crates/gpui/src/platform/mac/fonts.rs              |  671 -
crates/gpui/src/platform/mac/fonts/open_type.rs    |  395 
crates/gpui/src/platform/mac/geometry.rs           |   45 
crates/gpui/src/platform/mac/image_cache.rs        |  115 
crates/gpui/src/platform/mac/metal_atlas.rs        |    0 
crates/gpui/src/platform/mac/metal_renderer.rs     |    0 
crates/gpui/src/platform/mac/open_type.rs          |    0 
crates/gpui/src/platform/mac/platform.rs           |  720 
crates/gpui/src/platform/mac/renderer.rs           | 1277 --
crates/gpui/src/platform/mac/screen.rs             |  144 
crates/gpui/src/platform/mac/shaders.metal         |    0 
crates/gpui/src/platform/mac/shaders/shaders.h     |  135 
crates/gpui/src/platform/mac/shaders/shaders.metal |  464 
crates/gpui/src/platform/mac/sprite_cache.rs       |  164 
crates/gpui/src/platform/mac/text_system.rs        |    0 
crates/gpui/src/platform/mac/window.rs             |  604 
crates/gpui/src/platform/mac/window_appearence.rs  |    0 
crates/gpui/src/platform/test.rs                   |  443 
crates/gpui/src/platform/test/dispatcher.rs        |    0 
crates/gpui/src/platform/test/display.rs           |    0 
crates/gpui/src/platform/test/platform.rs          |    0 
crates/gpui/src/platform/test/window.rs            |    0 
crates/gpui/src/prelude.rs                         |    0 
crates/gpui/src/scene.rs                           | 1097 +
crates/gpui/src/scene/mouse_event.rs               |  270 
crates/gpui/src/scene/mouse_region.rs              |  555 -
crates/gpui/src/scene/region.rs                    |    7 
crates/gpui/src/shared_string.rs                   |    0 
crates/gpui/src/style.rs                           |    0 
crates/gpui/src/styled.rs                          |    0 
crates/gpui/src/subscription.rs                    |    0 
crates/gpui/src/svg_renderer.rs                    |    0 
crates/gpui/src/taffy.rs                           |    0 
crates/gpui/src/test.rs                            |  240 
crates/gpui/src/text_layout.rs                     |  876 -
crates/gpui/src/text_system.rs                     |    0 
crates/gpui/src/text_system/font_features.rs       |    0 
crates/gpui/src/text_system/line.rs                |    0 
crates/gpui/src/text_system/line_layout.rs         |    0 
crates/gpui/src/text_system/line_wrapper.rs        |    0 
crates/gpui/src/util.rs                            |   15 
crates/gpui/src/view.rs                            |    0 
crates/gpui/src/views.rs                           |    5 
crates/gpui/src/views/select.rs                    |  156 
crates/gpui/src/window.rs                          |    0 
crates/gpui/tests/action_macros.rs                 |    0 
crates/gpui/tests/test.rs                          |   14 
crates/gpui2/Cargo.toml                            |   92 
crates/gpui2/build.rs                              |  137 
crates/gpui2/src/app.rs                            | 1264 --
crates/gpui2/src/assets.rs                         |   64 
crates/gpui2/src/color.rs                          |  457 
crates/gpui2/src/elements/canvas.rs                |   54 
crates/gpui2/src/elements/list.rs                  |  560 -
crates/gpui2/src/elements/overlay.rs               |  241 
crates/gpui2/src/elements/svg.rs                   |   78 
crates/gpui2/src/elements/text.rs                  |  423 
crates/gpui2/src/elements/uniform_list.rs          |  316 
crates/gpui2/src/executor.rs                       |  402 
crates/gpui2/src/geometry.rs                       | 2796 -----
crates/gpui2/src/gpui2.rs                          |  215 
crates/gpui2/src/image_cache.rs                    |  107 
crates/gpui2/src/platform.rs                       |  592 -
crates/gpui2/src/platform/mac.rs                   |  139 
crates/gpui2/src/platform/mac/dispatch.h           |    1 
crates/gpui2/src/platform/mac/dispatcher.rs        |   96 
crates/gpui2/src/platform/mac/platform.rs          | 1194 --
crates/gpui2/src/platform/mac/window.rs            | 1791 ---
crates/gpui2/src/platform/test.rs                  |    9 
crates/gpui2/src/scene.rs                          |  778 -
crates/gpui2/src/test.rs                           |   80 
crates/gpui2/src/util.rs                           |   51 
crates/install_cli/Cargo.toml                      |    2 
crates/journal/Cargo.toml                          |    2 
crates/language/Cargo.toml                         |    4 
crates/language_selector/Cargo.toml                |    2 
crates/language_tools/Cargo.toml                   |    4 
crates/live_kit_client/Cargo.toml                  |    4 
crates/lsp/Cargo.toml                              |    4 
crates/menu/Cargo.toml                             |    2 
crates/multi_buffer/Cargo.toml                     |    4 
crates/notifications/Cargo.toml                    |    4 
crates/outline/Cargo.toml                          |    2 
crates/picker/Cargo.toml                           |    4 
crates/prettier/Cargo.toml                         |    4 
crates/project/Cargo.toml                          |    4 
crates/project_panel/Cargo.toml                    |    4 
crates/project_symbols/Cargo.toml                  |    4 
crates/quick_action_bar/Cargo.toml                 |    4 
crates/recent_projects/Cargo.toml                  |    2 
crates/rich_text/Cargo.toml                        |    2 
crates/rope/Cargo.toml                             |    2 
crates/rpc/Cargo.toml                              |    4 
crates/search/Cargo.toml                           |    4 
crates/semantic_index/Cargo.toml                   |    4 
crates/settings/Cargo.toml                         |    4 
crates/story/Cargo.toml                            |    2 
crates/storybook/Cargo.toml                        |    4 
crates/terminal/Cargo.toml                         |    2 
crates/terminal_view/Cargo.toml                    |    4 
crates/text/Cargo.toml                             |    2 
crates/theme/Cargo.toml                            |    4 
crates/theme_importer/Cargo.toml                   |    2 
crates/theme_selector/Cargo.toml                   |    2 
crates/ui/Cargo.toml                               |    2 
crates/util/Cargo.toml                             |    6 
crates/util/src/util.rs                            |   20 
crates/vcs_menu/Cargo.toml                         |    2 
crates/vim/Cargo.toml                              |    4 
crates/welcome/Cargo.toml                          |    2 
crates/workspace/Cargo.toml                        |    4 
crates/zed/Cargo.toml                              |    4 
crates/zed_actions/Cargo.toml                      |    2 
docs/src/telemetry.md                              |    2 
225 files changed, 7,735 insertions(+), 40,503 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -10,7 +10,7 @@ dependencies = [
  "auto_update",
  "editor",
  "futures 0.3.28",
- "gpui2",
+ "gpui",
  "language",
  "project",
  "settings",
@@ -82,7 +82,7 @@ dependencies = [
  "async-trait",
  "bincode",
  "futures 0.3.28",
- "gpui2",
+ "gpui",
  "isahc",
  "language",
  "lazy_static",
@@ -312,7 +312,7 @@ dependencies = [
  "env_logger",
  "fs",
  "futures 0.3.28",
- "gpui2",
+ "gpui",
  "indoc",
  "isahc",
  "language",
@@ -662,7 +662,7 @@ dependencies = [
  "anyhow",
  "collections",
  "futures 0.3.28",
- "gpui2",
+ "gpui",
  "log",
  "parking_lot 0.11.2",
  "rodio",
@@ -676,7 +676,7 @@ dependencies = [
  "anyhow",
  "client",
  "db",
- "gpui2",
+ "gpui",
  "isahc",
  "lazy_static",
  "log",
@@ -1010,7 +1010,7 @@ version = "0.1.0"
 dependencies = [
  "collections",
  "editor",
- "gpui2",
+ "gpui",
  "itertools 0.10.5",
  "language",
  "outline",
@@ -1111,7 +1111,7 @@ dependencies = [
  "collections",
  "fs",
  "futures 0.3.28",
- "gpui2",
+ "gpui",
  "image",
  "language",
  "live_kit_client",
@@ -1200,7 +1200,7 @@ dependencies = [
  "db",
  "feature_flags",
  "futures 0.3.28",
- "gpui2",
+ "gpui",
  "image",
  "language",
  "lazy_static",
@@ -1374,7 +1374,7 @@ dependencies = [
  "db",
  "feature_flags",
  "futures 0.3.28",
- "gpui2",
+ "gpui",
  "image",
  "lazy_static",
  "log",
@@ -1470,7 +1470,7 @@ dependencies = [
  "fs",
  "futures 0.3.28",
  "git",
- "gpui2",
+ "gpui",
  "hyper",
  "indoc",
  "language",
@@ -1534,7 +1534,7 @@ dependencies = [
  "feedback",
  "futures 0.3.28",
  "fuzzy",
- "gpui2",
+ "gpui",
  "language",
  "lazy_static",
  "log",
@@ -1603,7 +1603,7 @@ dependencies = [
  "env_logger",
  "fuzzy",
  "go_to_line",
- "gpui2",
+ "gpui",
  "language",
  "menu",
  "picker",
@@ -1702,7 +1702,7 @@ dependencies = [
  "collections",
  "fs",
  "futures 0.3.28",
- "gpui2",
+ "gpui",
  "language",
  "log",
  "lsp",
@@ -1727,7 +1727,7 @@ dependencies = [
  "editor",
  "fs",
  "futures 0.3.28",
- "gpui2",
+ "gpui",
  "language",
  "settings",
  "smol",
@@ -2127,7 +2127,7 @@ dependencies = [
  "async-trait",
  "collections",
  "env_logger",
- "gpui2",
+ "gpui",
  "indoc",
  "lazy_static",
  "log",
@@ -2229,7 +2229,7 @@ dependencies = [
  "collections",
  "editor",
  "futures 0.3.28",
- "gpui2",
+ "gpui",
  "language",
  "log",
  "lsp",
@@ -2397,7 +2397,7 @@ dependencies = [
  "futures 0.3.28",
  "fuzzy",
  "git",
- "gpui2",
+ "gpui",
  "indoc",
  "itertools 0.10.5",
  "language",
@@ -2606,7 +2606,7 @@ name = "feature_flags"
 version = "0.1.0"
 dependencies = [
  "anyhow",
- "gpui2",
+ "gpui",
 ]
 
 [[package]]
@@ -2619,7 +2619,7 @@ dependencies = [
  "db",
  "editor",
  "futures 0.3.28",
- "gpui2",
+ "gpui",
  "human_bytes",
  "isahc",
  "language",
@@ -2653,7 +2653,7 @@ dependencies = [
  "editor",
  "env_logger",
  "fuzzy",
- "gpui2",
+ "gpui",
  "language",
  "menu",
  "picker",
@@ -2822,7 +2822,7 @@ dependencies = [
  "fsevent",
  "futures 0.3.28",
  "git2",
- "gpui2",
+ "gpui",
  "lazy_static",
  "libc",
  "log",
@@ -3014,7 +3014,7 @@ dependencies = [
 name = "fuzzy"
 version = "0.1.0"
 dependencies = [
- "gpui2",
+ "gpui",
  "util",
 ]
 
@@ -3149,7 +3149,7 @@ name = "go_to_line"
 version = "0.1.0"
 dependencies = [
  "editor",
- "gpui2",
+ "gpui",
  "menu",
  "postage",
  "serde",
@@ -3164,68 +3164,6 @@ dependencies = [
 [[package]]
 name = "gpui"
 version = "0.1.0"
-dependencies = [
- "anyhow",
- "async-task",
- "backtrace",
- "bindgen 0.65.1",
- "block",
- "cc",
- "cocoa",
- "collections",
- "core-foundation",
- "core-graphics",
- "core-text",
- "ctor",
- "derive_more",
- "dhat",
- "env_logger",
- "etagere",
- "font-kit",
- "foreign-types",
- "futures 0.3.28",
- "gpui_macros",
- "image",
- "itertools 0.10.5",
- "lazy_static",
- "log",
- "media",
- "metal",
- "num_cpus",
- "objc",
- "ordered-float 2.10.0",
- "parking",
- "parking_lot 0.11.2",
- "pathfinder_color",
- "pathfinder_geometry",
- "png",
- "postage",
- "rand 0.8.5",
- "refineable",
- "resvg",
- "schemars",
- "seahash",
- "serde",
- "serde_derive",
- "serde_json",
- "simplelog",
- "smallvec",
- "smol",
- "sqlez",
- "sum_tree",
- "taffy 0.3.11 (git+https://github.com/DioxusLabs/taffy?rev=4fb530bdd71609bb1d3f76c6a8bde1ba82805d5e)",
- "thiserror",
- "time",
- "tiny-skia",
- "usvg",
- "util",
- "uuid 1.4.1",
- "waker-fn",
-]
-
-[[package]]
-name = "gpui2"
-version = "0.1.0"
 dependencies = [
  "anyhow",
  "async-task",
@@ -3277,7 +3215,7 @@ dependencies = [
  "smol",
  "sqlez",
  "sum_tree",
- "taffy 0.3.11 (git+https://github.com/DioxusLabs/taffy?rev=1876f72bee5e376023eaa518aa7b8a34c769bd1b)",
+ "taffy",
  "thiserror",
  "time",
  "tiny-skia",
@@ -3306,12 +3244,6 @@ dependencies = [
  "syn 1.0.109",
 ]
 
-[[package]]
-name = "grid"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eec1c01eb1de97451ee0d60de7d81cf1e72aabefb021616027f3d1c3ec1c723c"
-
 [[package]]
 name = "grid"
 version = "0.11.0"
@@ -3694,7 +3626,7 @@ name = "install_cli"
 version = "0.1.0"
 dependencies = [
  "anyhow",
- "gpui2",
+ "gpui",
  "log",
  "smol",
  "util",
@@ -3847,7 +3779,7 @@ dependencies = [
  "chrono",
  "dirs 4.0.0",
  "editor",
- "gpui2",
+ "gpui",
  "log",
  "schemars",
  "serde",
@@ -3940,7 +3872,7 @@ dependencies = [
  "fuzzy",
  "git",
  "globset",
- "gpui2",
+ "gpui",
  "indoc",
  "lazy_static",
  "log",
@@ -3985,7 +3917,7 @@ dependencies = [
  "anyhow",
  "editor",
  "fuzzy",
- "gpui2",
+ "gpui",
  "language",
  "picker",
  "project",
@@ -4006,7 +3938,7 @@ dependencies = [
  "editor",
  "env_logger",
  "futures 0.3.28",
- "gpui2",
+ "gpui",
  "language",
  "lsp",
  "project",
@@ -4181,7 +4113,7 @@ dependencies = [
  "core-graphics",
  "foreign-types",
  "futures 0.3.28",
- "gpui2",
+ "gpui",
  "hmac 0.12.1",
  "jwt",
  "live_kit_server",
@@ -4247,7 +4179,7 @@ dependencies = [
  "ctor",
  "env_logger",
  "futures 0.3.28",
- "gpui2",
+ "gpui",
  "log",
  "lsp-types",
  "parking_lot 0.11.2",
@@ -4399,7 +4331,7 @@ dependencies = [
 name = "menu"
 version = "0.1.0"
 dependencies = [
- "gpui2",
+ "gpui",
  "serde",
 ]
 
@@ -4569,7 +4501,7 @@ dependencies = [
  "env_logger",
  "futures 0.3.28",
  "git",
- "gpui2",
+ "gpui",
  "indoc",
  "itertools 0.10.5",
  "language",
@@ -4761,7 +4693,7 @@ dependencies = [
  "collections",
  "db",
  "feature_flags",
- "gpui2",
+ "gpui",
  "rpc",
  "settings",
  "sum_tree",
@@ -5162,7 +5094,7 @@ version = "0.1.0"
 dependencies = [
  "editor",
  "fuzzy",
- "gpui2",
+ "gpui",
  "language",
  "ordered-float 2.10.0",
  "picker",
@@ -5386,7 +5318,7 @@ dependencies = [
  "ctor",
  "editor",
  "env_logger",
- "gpui2",
+ "gpui",
  "menu",
  "parking_lot 0.11.2",
  "serde_json",
@@ -5570,7 +5502,7 @@ dependencies = [
  "collections",
  "fs",
  "futures 0.3.28",
- "gpui2",
+ "gpui",
  "language",
  "log",
  "lsp",
@@ -5687,7 +5619,7 @@ dependencies = [
  "git",
  "git2",
  "globset",
- "gpui2",
+ "gpui",
  "ignore",
  "itertools 0.10.5",
  "language",
@@ -5730,7 +5662,7 @@ dependencies = [
  "db",
  "editor",
  "futures 0.3.28",
- "gpui2",
+ "gpui",
  "language",
  "menu",
  "postage",
@@ -5758,7 +5690,7 @@ dependencies = [
  "editor",
  "futures 0.3.28",
  "fuzzy",
- "gpui2",
+ "gpui",
  "language",
  "lsp",
  "ordered-float 2.10.0",
@@ -5935,7 +5867,7 @@ version = "0.1.0"
 dependencies = [
  "assistant",
  "editor",
- "gpui2",
+ "gpui",
  "search",
  "ui",
  "workspace",
@@ -6109,7 +6041,7 @@ dependencies = [
  "editor",
  "futures 0.3.28",
  "fuzzy",
- "gpui2",
+ "gpui",
  "language",
  "ordered-float 2.10.0",
  "picker",
@@ -6306,7 +6238,7 @@ dependencies = [
  "anyhow",
  "collections",
  "futures 0.3.28",
- "gpui2",
+ "gpui",
  "language",
  "lazy_static",
  "pulldown-cmark",
@@ -6397,7 +6329,7 @@ version = "0.1.0"
 dependencies = [
  "arrayvec 0.7.4",
  "bromberg_sl2",
- "gpui2",
+ "gpui",
  "log",
  "rand 0.8.5",
  "smallvec",
@@ -6427,7 +6359,7 @@ dependencies = [
  "ctor",
  "env_logger",
  "futures 0.3.28",
- "gpui2",
+ "gpui",
  "parking_lot 0.11.2",
  "prost 0.8.0",
  "prost-build",
@@ -6887,7 +6819,7 @@ dependencies = [
  "collections",
  "editor",
  "futures 0.3.28",
- "gpui2",
+ "gpui",
  "language",
  "log",
  "menu",
@@ -6943,7 +6875,7 @@ dependencies = [
  "env_logger",
  "futures 0.3.28",
  "globset",
- "gpui2",
+ "gpui",
  "language",
  "lazy_static",
  "log",
@@ -7110,7 +7042,7 @@ dependencies = [
  "feature_flags",
  "fs",
  "futures 0.3.28",
- "gpui2",
+ "gpui",
  "indoc",
  "lazy_static",
  "postage",
@@ -7714,7 +7646,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
 name = "story"
 version = "0.1.0"
 dependencies = [
- "gpui2",
+ "gpui",
  "itertools 0.10.5",
  "smallvec",
 ]
@@ -7730,7 +7662,7 @@ dependencies = [
  "dialoguer",
  "editor",
  "fuzzy",
- "gpui2",
+ "gpui",
  "indoc",
  "itertools 0.11.0",
  "language",
@@ -7957,18 +7889,7 @@ version = "0.3.11"
 source = "git+https://github.com/DioxusLabs/taffy?rev=1876f72bee5e376023eaa518aa7b8a34c769bd1b#1876f72bee5e376023eaa518aa7b8a34c769bd1b"
 dependencies = [
  "arrayvec 0.7.4",
- "grid 0.11.0",
- "num-traits",
- "slotmap",
-]
-
-[[package]]
-name = "taffy"
-version = "0.3.11"
-source = "git+https://github.com/DioxusLabs/taffy?rev=4fb530bdd71609bb1d3f76c6a8bde1ba82805d5e#4fb530bdd71609bb1d3f76c6a8bde1ba82805d5e"
-dependencies = [
- "arrayvec 0.7.4",
- "grid 0.10.0",
+ "grid",
  "num-traits",
  "slotmap",
 ]
@@ -8032,7 +7953,7 @@ dependencies = [
  "db",
  "dirs 4.0.0",
  "futures 0.3.28",
- "gpui2",
+ "gpui",
  "itertools 0.10.5",
  "lazy_static",
  "libc",
@@ -8062,7 +7983,7 @@ dependencies = [
  "dirs 4.0.0",
  "editor",
  "futures 0.3.28",
- "gpui2",
+ "gpui",
  "itertools 0.10.5",
  "language",
  "lazy_static",
@@ -8096,7 +8017,7 @@ dependencies = [
  "ctor",
  "digest 0.9.0",
  "env_logger",
- "gpui2",
+ "gpui",
  "lazy_static",
  "log",
  "parking_lot 0.11.2",
@@ -8121,7 +8042,7 @@ version = "0.1.0"
 dependencies = [
  "anyhow",
  "fs",
- "gpui2",
+ "gpui",
  "indexmap 1.9.3",
  "itertools 0.11.0",
  "parking_lot 0.11.2",
@@ -8145,7 +8066,7 @@ dependencies = [
  "anyhow",
  "clap 4.4.4",
  "convert_case 0.6.0",
- "gpui2",
+ "gpui",
  "indexmap 1.9.3",
  "json_comments",
  "log",
@@ -8168,7 +8089,7 @@ dependencies = [
  "feature_flags",
  "fs",
  "fuzzy",
- "gpui2",
+ "gpui",
  "log",
  "parking_lot 0.11.2",
  "picker",
@@ -8985,7 +8906,7 @@ version = "0.1.0"
 dependencies = [
  "anyhow",
  "chrono",
- "gpui2",
+ "gpui",
  "itertools 0.11.0",
  "menu",
  "rand 0.8.5",
@@ -9238,7 +9159,7 @@ dependencies = [
  "anyhow",
  "fs",
  "fuzzy",
- "gpui2",
+ "gpui",
  "picker",
  "ui",
  "util",
@@ -9263,7 +9184,7 @@ dependencies = [
  "diagnostics",
  "editor",
  "futures 0.3.28",
- "gpui2",
+ "gpui",
  "indoc",
  "itertools 0.10.5",
  "language",
@@ -9682,7 +9603,7 @@ dependencies = [
  "editor",
  "fs",
  "fuzzy",
- "gpui2",
+ "gpui",
  "install_cli",
  "log",
  "picker",
@@ -9951,7 +9872,7 @@ dependencies = [
  "env_logger",
  "fs",
  "futures 0.3.28",
- "gpui2",
+ "gpui",
  "indoc",
  "install_cli",
  "itertools 0.10.5",
@@ -10091,7 +10012,7 @@ dependencies = [
  "fsevent",
  "futures 0.3.28",
  "go_to_line",
- "gpui2",
+ "gpui",
  "ignore",
  "image",
  "indexmap 1.9.3",
@@ -10187,7 +10108,7 @@ dependencies = [
 name = "zed_actions"
 version = "0.1.0"
 dependencies = [
- "gpui2",
+ "gpui",
  "serde",
 ]
 

Cargo.toml 🔗

@@ -35,7 +35,7 @@ members = [
     "crates/go_to_line",
     "crates/gpui",
     "crates/gpui_macros",
-    "crates/gpui2",
+    "crates/gpui",
     "crates/gpui2_macros",
     "crates/install_cli",
     "crates/journal",

crates/activity_indicator/Cargo.toml 🔗

@@ -12,7 +12,7 @@ doctest = false
 auto_update = { path = "../auto_update" }
 editor = { path = "../editor" }
 language = { path = "../language" }
-gpui = { path = "../gpui2", package = "gpui2" }
+gpui = { path = "../gpui" }
 project = { path = "../project" }
 settings = { path = "../settings" }
 ui = { path = "../ui" }

crates/ai/Cargo.toml 🔗

@@ -12,7 +12,7 @@ doctest = false
 test-support = []
 
 [dependencies]
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 util = { path = "../util" }
 language = { path = "../language" }
 async-trait.workspace = true
@@ -35,4 +35,4 @@ rusqlite = { version = "0.29.0", features = ["blob", "array", "modern_sqlite"] }
 bincode = "1.3.3"
 
 [dev-dependencies]
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }

crates/assistant/Cargo.toml 🔗

@@ -14,7 +14,7 @@ client = { path = "../client" }
 collections = { path = "../collections"}
 editor = { path = "../editor" }
 fs = { path = "../fs" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 language = { path = "../language" }
 menu = { path = "../menu" }
 multi_buffer = { path = "../multi_buffer" }

crates/audio/Cargo.toml 🔗

@@ -9,7 +9,7 @@ path = "src/audio.rs"
 doctest = false
 
 [dependencies]
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 collections = { path = "../collections" }
 util = { path = "../util" }
 

crates/auto_update/Cargo.toml 🔗

@@ -11,7 +11,7 @@ doctest = false
 [dependencies]
 db = { path = "../db" }
 client = { path = "../client" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 menu = { path = "../menu" }
 project = { path = "../project" }
 settings = { path = "../settings" }

crates/breadcrumbs/Cargo.toml 🔗

@@ -11,7 +11,7 @@ doctest = false
 [dependencies]
 collections = { path = "../collections" }
 editor = { path = "../editor" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 ui = { path = "../ui" }
 language = { path = "../language" }
 project = { path = "../project" }
@@ -24,5 +24,5 @@ itertools = "0.10"
 
 [dev-dependencies]
 editor = { path = "../editor", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 workspace = { path = "../workspace", features = ["test-support"] }

crates/call/Cargo.toml 🔗

@@ -22,7 +22,7 @@ test-support = [
 audio = { path = "../audio" }
 client = { path = "../client" }
 collections = { path = "../collections" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 log.workspace = true
 live_kit_client = { path = "../live_kit_client" }
 fs = { path = "../fs" }
@@ -48,7 +48,7 @@ client = { path = "../client", features = ["test-support"] }
 fs = { path = "../fs", features = ["test-support"] }
 language = { path = "../language", features = ["test-support"] }
 collections = { path = "../collections", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 live_kit_client = { path = "../live_kit_client", features = ["test-support"] }
 project = { path = "../project", features = ["test-support"] }
 util = { path = "../util", features = ["test-support"] }

crates/channel/Cargo.toml 🔗

@@ -15,7 +15,7 @@ test-support = ["collections/test-support", "gpui/test-support", "rpc/test-suppo
 client = { path = "../client" }
 collections = { path = "../collections" }
 db = { path = "../db" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 util = { path = "../util" }
 rpc = { path = "../rpc" }
 text = { path = "../text" }
@@ -47,7 +47,7 @@ tempfile = "3"
 
 [dev-dependencies]
 collections = { path = "../collections", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 rpc = { path = "../rpc", features = ["test-support"] }
 client = { path = "../client", features = ["test-support"] }
 settings = { path = "../settings", features = ["test-support"] }

crates/client/Cargo.toml 🔗

@@ -15,7 +15,7 @@ test-support = ["collections/test-support", "gpui/test-support", "rpc/test-suppo
 chrono = { version = "0.4", features = ["serde"] }
 collections = { path = "../collections" }
 db = { path = "../db" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 util = { path = "../util" }
 rpc = { path = "../rpc" }
 text = { path = "../text" }
@@ -47,7 +47,7 @@ url = "2.2"
 
 [dev-dependencies]
 collections = { path = "../collections", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 rpc = { path = "../rpc", features = ["test-support"] }
 settings = { path = "../settings", features = ["test-support"] }
 util = { path = "../util", features = ["test-support"] }

crates/collab/Cargo.toml 🔗

@@ -62,7 +62,7 @@ uuid.workspace = true
 [dev-dependencies]
 audio = { path = "../audio" }
 collections = { path = "../collections", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 call = { path = "../call", features = ["test-support"] }
 client = { path = "../client", features = ["test-support"] }
 channel = { path = "../channel" }

crates/collab2/Cargo.toml 🔗

@@ -0,0 +1,99 @@
+[package]
+authors = ["Nathan Sobo <nathan@zed.dev>"]
+default-run = "collab"
+edition = "2021"
+name = "collab"
+version = "0.28.0"
+publish = false
+
+[[bin]]
+name = "collab"
+
+[[bin]]
+name = "seed"
+required-features = ["seed-support"]
+
+[dependencies]
+clock = { path = "../clock" }
+collections = { path = "../collections" }
+live_kit_server = { path = "../live_kit_server" }
+text = { path = "../text" }
+rpc = { path = "../rpc" }
+util = { path = "../util" }
+
+anyhow.workspace = true
+async-tungstenite = "0.16"
+axum = { version = "0.5", features = ["json", "headers", "ws"] }
+axum-extra = { version = "0.3", features = ["erased-json"] }
+base64 = "0.13"
+clap = { version = "3.1", features = ["derive"], optional = true }
+dashmap = "5.4"
+envy = "0.4.2"
+futures.workspace = true
+hyper = "0.14"
+lazy_static.workspace = true
+lipsum = { version = "0.8", optional = true }
+log.workspace = true
+nanoid = "0.4"
+parking_lot.workspace = true
+prometheus = "0.13"
+prost.workspace = true
+rand.workspace = true
+reqwest = { version = "0.11", features = ["json"], optional = true }
+scrypt = "0.7"
+smallvec.workspace = true
+sea-orm = { version = "0.12.x", features = ["sqlx-postgres", "postgres-array", "runtime-tokio-rustls", "with-uuid"] }
+serde.workspace = true
+serde_derive.workspace = true
+serde_json.workspace = true
+sha-1 = "0.9"
+sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres", "json", "time", "uuid", "any"] }
+time.workspace = true
+tokio = { version = "1", features = ["full"] }
+tokio-tungstenite = "0.17"
+tonic = "0.6"
+tower = "0.4"
+toml.workspace = true
+tracing = "0.1.34"
+tracing-log = "0.1.3"
+tracing-subscriber = { version = "0.3.11", features = ["env-filter", "json"] }
+uuid.workspace = true
+
+[dev-dependencies]
+audio = { path = "../audio" }
+collections = { path = "../collections", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
+call = { path = "../call", features = ["test-support"] }
+client = { path = "../client", features = ["test-support"] }
+channel = { path = "../channel" }
+editor = { path = "../editor", features = ["test-support"] }
+language = { path = "../language", features = ["test-support"] }
+fs = { path = "../fs", features = ["test-support"] }
+git = { path = "../git", features = ["test-support"] }
+live_kit_client = { path = "../live_kit_client", features = ["test-support"] }
+lsp = { path = "../lsp", features = ["test-support"] }
+node_runtime = { path = "../node_runtime" }
+notifications = { path = "../notifications", features = ["test-support"] }
+
+project = { path = "../project", features = ["test-support"] }
+rpc = { path = "../rpc", features = ["test-support"] }
+settings = { path = "../settings", features = ["test-support"] }
+theme = { path = "../theme" }
+workspace = { path = "../workspace", features = ["test-support"] }
+
+collab_ui = { path = "../collab_ui", features = ["test-support"] }
+
+async-trait.workspace = true
+pretty_assertions.workspace = true
+ctor.workspace = true
+env_logger.workspace = true
+indoc.workspace = true
+util = { path = "../util" }
+lazy_static.workspace = true
+sea-orm = { version = "0.12.x", features = ["sqlx-sqlite"] }
+serde_json.workspace = true
+sqlx = { version = "0.7", features = ["sqlite"] }
+unindent.workspace = true
+
+[features]
+seed-support = ["clap", "lipsum", "reqwest"]

crates/collab_ui/Cargo.toml 🔗

@@ -34,7 +34,7 @@ collections = { path = "../collections" }
 editor = { path = "../editor" }
 feedback = { path = "../feedback" }
 fuzzy = {  path = "../fuzzy" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 language = { path = "../language" }
 menu = {  path = "../menu" }
 notifications = { path = "../notifications" }
@@ -69,7 +69,7 @@ call = { path = "../call", features = ["test-support"] }
 client = { path = "../client", features = ["test-support"] }
 collections = { path = "../collections", features = ["test-support"] }
 editor = { path = "../editor", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 notifications = { path = "../notifications", features = ["test-support"] }
 project = { path = "../project", features = ["test-support"] }
 rpc = { path = "../rpc", features = ["test-support"] }

crates/command_palette/Cargo.toml 🔗

@@ -12,7 +12,7 @@ doctest = false
 collections = { path = "../collections" }
 editor = { path = "../editor" }
 fuzzy = {  path = "../fuzzy" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 picker = { path = "../picker" }
 project = { path = "../project" }
 settings = { path = "../settings" }
@@ -25,7 +25,7 @@ anyhow.workspace = true
 serde.workspace = true
 
 [dev-dependencies]
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 editor = { path = "../editor", features = ["test-support"] }
 language = { path = "../language", features = ["test-support"] }
 project = { path = "../project", features = ["test-support"] }

crates/copilot/Cargo.toml 🔗

@@ -21,7 +21,7 @@ test-support = [
 [dependencies]
 collections = { path = "../collections" }
 # context_menu = { path = "../context_menu" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 language = { path = "../language" }
 settings = { path = "../settings" }
 theme = { path = "../theme" }
@@ -43,7 +43,7 @@ parking_lot.workspace = true
 clock = { path = "../clock" }
 collections = { path = "../collections", features = ["test-support"] }
 fs = { path = "../fs", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 language = { path = "../language", features = ["test-support"] }
 lsp = { path = "../lsp", features = ["test-support"] }
 rpc = { path = "../rpc", features = ["test-support"] }

crates/copilot_button/Cargo.toml 🔗

@@ -13,7 +13,7 @@ copilot = { path = "../copilot" }
 editor = { path = "../editor" }
 fs = { path = "../fs" }
 zed_actions = { path = "../zed_actions"}
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 language = { path = "../language" }
 settings = { path = "../settings" }
 theme = { path = "../theme" }

crates/db/Cargo.toml 🔗

@@ -13,7 +13,7 @@ test-support = []
 
 [dependencies]
 collections = { path = "../collections" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 sqlez = { path = "../sqlez" }
 sqlez_macros = { path = "../sqlez_macros" }
 util = { path = "../util" }
@@ -28,6 +28,6 @@ serde_derive.workspace = true
 smol.workspace = true
 
 [dev-dependencies]
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 env_logger.workspace = true
 tempdir.workspace = true

crates/diagnostics/Cargo.toml 🔗

@@ -11,7 +11,7 @@ doctest = false
 [dependencies]
 collections = { path = "../collections" }
 editor = { path = "../editor" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 ui = { path = "../ui" }
 language = { path = "../language" }
 lsp = { path = "../lsp" }
@@ -35,7 +35,7 @@ client = { path = "../client", features = ["test-support"] }
 editor = { path = "../editor", features = ["test-support"] }
 language = { path = "../language", features = ["test-support"] }
 lsp = { path = "../lsp", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 workspace = {path = "../workspace", features = ["test-support"] }
 theme = { path = "../theme", features = ["test-support"] }
 

crates/editor/Cargo.toml 🔗

@@ -31,7 +31,7 @@ collections = { path = "../collections" }
 # context_menu = { path = "../context_menu" }
 fuzzy = {  path = "../fuzzy" }
 git = { path = "../git" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 language = { path = "../language" }
 lsp = { path = "../lsp" }
 multi_buffer = { path = "../multi_buffer" }
@@ -76,7 +76,7 @@ copilot = { path = "../copilot", features = ["test-support"] }
 text = { path = "../text", features = ["test-support"] }
 language = { path = "../language", features = ["test-support"] }
 lsp = { path = "../lsp", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 util = { path = "../util", features = ["test-support"] }
 project = { path = "../project", features = ["test-support"] }
 settings = { path = "../settings", features = ["test-support"] }

crates/feature_flags/Cargo.toml 🔗

@@ -8,5 +8,5 @@ publish = false
 path = "src/feature_flags.rs"
 
 [dependencies]
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 anyhow.workspace = true

crates/feedback/Cargo.toml 🔗

@@ -14,7 +14,7 @@ test-support = []
 client = { path = "../client" }
 db = { path = "../db" }
 editor = { path = "../editor" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 language = { path = "../language" }
 menu = { path = "../menu" }
 project = { path = "../project" }

crates/file_finder/Cargo.toml 🔗

@@ -12,7 +12,7 @@ doctest = false
 editor = { path = "../editor" }
 collections = { path = "../collections" }
 fuzzy = {  path = "../fuzzy" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 menu = { path = "../menu" }
 picker = { path = "../picker" }
 project = { path = "../project" }
@@ -27,7 +27,7 @@ serde.workspace = true
 
 [dev-dependencies]
 editor = { path = "../editor", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 language = { path = "../language", features = ["test-support"] }
 workspace = { path = "../workspace", features = ["test-support"] }
 theme = { path = "../theme", features = ["test-support"] }

crates/fs/Cargo.toml 🔗

@@ -31,10 +31,10 @@ log.workspace = true
 libc = "0.2"
 time.workspace = true
 
-gpui = { package = "gpui2", path = "../gpui2", optional = true}
+gpui = { path = "../gpui", optional = true}
 
 [dev-dependencies]
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 
 [features]
 test-support = ["gpui/test-support"]

crates/fuzzy/Cargo.toml 🔗

@@ -9,5 +9,5 @@ path = "src/fuzzy.rs"
 doctest = false
 
 [dependencies]
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 util = { path = "../util" }

crates/go_to_line/Cargo.toml 🔗

@@ -10,7 +10,7 @@ doctest = false
 
 [dependencies]
 editor = { path = "../editor" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 menu = { path = "../menu" }
 serde.workspace = true
 settings = { path = "../settings" }

crates/gpui/Cargo.toml 🔗

@@ -1,27 +1,28 @@
 [package]
-authors = ["Nathan Sobo <nathansobo@gmail.com>"]
-edition = "2021"
 name = "gpui"
 version = "0.1.0"
-description = "A GPU-accelerated UI framework"
+edition = "2021"
+authors = ["Nathan Sobo <nathan@zed.dev>"]
+description = "The next version of Zed's GPU-accelerated UI framework"
 publish = false
 
+[features]
+test-support = ["backtrace", "dhat", "env_logger", "collections/test-support", "util/test-support"]
+
 [lib]
 path = "src/gpui.rs"
 doctest = false
 
-[features]
-test-support = ["backtrace", "dhat", "env_logger", "collections/test-support", "util/test-support"]
-
 [dependencies]
 collections = { path = "../collections" }
-gpui_macros = { path = "../gpui_macros" }
+gpui2_macros = { path = "../gpui2_macros" }
 util = { path = "../util" }
 sum_tree = { path = "../sum_tree" }
 sqlez = { path = "../sqlez" }
 async-task = "4.0.3"
 backtrace = { version = "0.3", optional = true }
 ctor.workspace = true
+linkme = "0.3"
 derive_more.workspace = true
 dhat = { version = "0.3", optional = true }
 env_logger = { version = "0.9", optional = true }
@@ -35,30 +36,27 @@ num_cpus = "1.13"
 ordered-float.workspace = true
 parking = "2.0.0"
 parking_lot.workspace = true
-pathfinder_color = "0.5"
 pathfinder_geometry = "0.5"
 postage.workspace = true
 rand.workspace = true
 refineable.workspace = true
 resvg = "0.14"
-schemars = "0.8"
 seahash = "4.1"
 serde.workspace = true
 serde_derive.workspace = true
 serde_json.workspace = true
 smallvec.workspace = true
 smol.workspace = true
-taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "4fb530bdd71609bb1d3f76c6a8bde1ba82805d5e" }
+taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "1876f72bee5e376023eaa518aa7b8a34c769bd1b" }
 thiserror.workspace = true
 time.workspace = true
 tiny-skia = "0.5"
 usvg = { version = "0.14", features = [] }
-uuid.workspace = true
+uuid = { version = "1.1.2", features = ["v4"] }
 waker-fn = "1.1.0"
-
-[build-dependencies]
-bindgen = "0.65.1"
-cc = "1.0.67"
+slotmap = "1.0.6"
+schemars.workspace = true
+bitflags = "2.4.0"
 
 [dev-dependencies]
 backtrace = "0.3"
@@ -69,6 +67,10 @@ png = "0.16"
 simplelog = "0.9"
 util = { path = "../util", features = ["test-support"] }
 
+[build-dependencies]
+bindgen = "0.65.1"
+cbindgen = "0.26.0"
+
 [target.'cfg(target_os = "macos")'.dependencies]
 media = { path = "../media" }
 anyhow.workspace = true

crates/gpui/build.rs 🔗

@@ -1,13 +1,15 @@
 use std::{
     env,
-    path::PathBuf,
+    path::{Path, PathBuf},
     process::{self, Command},
 };
 
+use cbindgen::Config;
+
 fn main() {
     generate_dispatch_bindings();
-    compile_metal_shaders();
-    generate_shader_bindings();
+    let header_path = generate_shader_bindings();
+    compile_metal_shaders(&header_path);
 }
 
 fn generate_dispatch_bindings() {
@@ -17,7 +19,12 @@ fn generate_dispatch_bindings() {
     let bindings = bindgen::Builder::default()
         .header("src/platform/mac/dispatch.h")
         .allowlist_var("_dispatch_main_q")
+        .allowlist_var("DISPATCH_QUEUE_PRIORITY_DEFAULT")
+        .allowlist_var("DISPATCH_TIME_NOW")
+        .allowlist_function("dispatch_get_global_queue")
         .allowlist_function("dispatch_async_f")
+        .allowlist_function("dispatch_after_f")
+        .allowlist_function("dispatch_time")
         .parse_callbacks(Box::new(bindgen::CargoCallbacks))
         .layout_tests(false)
         .generate()
@@ -29,14 +36,61 @@ fn generate_dispatch_bindings() {
         .expect("couldn't write dispatch bindings");
 }
 
-const SHADER_HEADER_PATH: &str = "./src/platform/mac/shaders/shaders.h";
+fn generate_shader_bindings() -> PathBuf {
+    let output_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("scene.h");
+    let crate_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
+    let mut config = Config::default();
+    config.include_guard = Some("SCENE_H".into());
+    config.language = cbindgen::Language::C;
+    config.export.include.extend([
+        "Bounds".into(),
+        "Corners".into(),
+        "Edges".into(),
+        "Size".into(),
+        "Pixels".into(),
+        "PointF".into(),
+        "Hsla".into(),
+        "ContentMask".into(),
+        "Uniforms".into(),
+        "AtlasTile".into(),
+        "PathRasterizationInputIndex".into(),
+        "PathVertex_ScaledPixels".into(),
+        "ShadowInputIndex".into(),
+        "Shadow".into(),
+        "QuadInputIndex".into(),
+        "Underline".into(),
+        "UnderlineInputIndex".into(),
+        "Quad".into(),
+        "SpriteInputIndex".into(),
+        "MonochromeSprite".into(),
+        "PolychromeSprite".into(),
+        "PathSprite".into(),
+        "SurfaceInputIndex".into(),
+        "SurfaceBounds".into(),
+    ]);
+    config.no_includes = true;
+    config.enumeration.prefix_with_name = true;
+    cbindgen::Builder::new()
+        .with_src(crate_dir.join("src/scene.rs"))
+        .with_src(crate_dir.join("src/geometry.rs"))
+        .with_src(crate_dir.join("src/color.rs"))
+        .with_src(crate_dir.join("src/window.rs"))
+        .with_src(crate_dir.join("src/platform.rs"))
+        .with_src(crate_dir.join("src/platform/mac/metal_renderer.rs"))
+        .with_config(config)
+        .generate()
+        .expect("Unable to generate bindings")
+        .write_to_file(&output_path);
+
+    output_path
+}
 
-fn compile_metal_shaders() {
-    let shader_path = "./src/platform/mac/shaders/shaders.metal";
+fn compile_metal_shaders(header_path: &Path) {
+    let shader_path = "./src/platform/mac/shaders.metal";
     let air_output_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("shaders.air");
     let metallib_output_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("shaders.metallib");
 
-    println!("cargo:rerun-if-changed={}", SHADER_HEADER_PATH);
+    println!("cargo:rerun-if-changed={}", header_path.display());
     println!("cargo:rerun-if-changed={}", shader_path);
 
     let output = Command::new("xcrun")
@@ -49,6 +103,8 @@ fn compile_metal_shaders() {
             "-MO",
             "-c",
             shader_path,
+            "-include",
+            &header_path.to_str().unwrap(),
             "-o",
         ])
         .arg(&air_output_path)
@@ -79,18 +135,3 @@ fn compile_metal_shaders() {
         process::exit(1);
     }
 }
-
-fn generate_shader_bindings() {
-    let bindings = bindgen::Builder::default()
-        .header(SHADER_HEADER_PATH)
-        .allowlist_type("GPUI.*")
-        .parse_callbacks(Box::new(bindgen::CargoCallbacks))
-        .layout_tests(false)
-        .generate()
-        .expect("unable to generate bindings");
-
-    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
-    bindings
-        .write_to_file(out_path.join("shaders.rs"))
-        .expect("couldn't write shader bindings");
-}

crates/gpui/examples/components.rs 🔗

@@ -1,237 +0,0 @@
-use button_component::Button;
-
-use gpui::{
-    color::Color,
-    elements::{ContainerStyle, Flex, Label, ParentElement, StatefulComponent},
-    fonts::{self, TextStyle},
-    platform::WindowOptions,
-    AnyElement, App, Element, Entity, View, ViewContext,
-};
-use log::LevelFilter;
-use pathfinder_geometry::vector::vec2f;
-use simplelog::SimpleLogger;
-use theme::Toggleable;
-use toggleable_button::ToggleableButton;
-
-// cargo run -p gpui --example components
-
-fn main() {
-    SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
-
-    App::new(()).unwrap().run(|cx| {
-        cx.platform().activate(true);
-        cx.add_window(WindowOptions::with_bounds(vec2f(300., 200.)), |_| {
-            TestView {
-                count: 0,
-                is_doubling: false,
-            }
-        });
-    });
-}
-
-pub struct TestView {
-    count: usize,
-    is_doubling: bool,
-}
-
-impl TestView {
-    fn increase_count(&mut self) {
-        if self.is_doubling {
-            self.count *= 2;
-        } else {
-            self.count += 1;
-        }
-    }
-}
-
-impl Entity for TestView {
-    type Event = ();
-}
-
-type ButtonStyle = ContainerStyle;
-
-impl View for TestView {
-    fn ui_name() -> &'static str {
-        "TestView"
-    }
-
-    fn render(&mut self, cx: &mut ViewContext<'_, '_, Self>) -> AnyElement<Self> {
-        fonts::with_font_cache(cx.font_cache.to_owned(), || {
-            Flex::column()
-                .with_child(Label::new(
-                    format!("Count: {}", self.count),
-                    TextStyle::for_color(Color::red()),
-                ))
-                .with_child(
-                    Button::new(move |_, v: &mut Self, cx| {
-                        v.increase_count();
-                        cx.notify();
-                    })
-                    .with_text(
-                        "Hello from a counting BUTTON",
-                        TextStyle::for_color(Color::blue()),
-                    )
-                    .with_style(ButtonStyle::fill(Color::yellow()))
-                    .element(),
-                )
-                .with_child(
-                    ToggleableButton::new(self.is_doubling, move |_, v: &mut Self, cx| {
-                        v.is_doubling = !v.is_doubling;
-                        cx.notify();
-                    })
-                    .with_text("Double the count?", TextStyle::for_color(Color::black()))
-                    .with_style(Toggleable {
-                        inactive: ButtonStyle::fill(Color::red()),
-                        active: ButtonStyle::fill(Color::green()),
-                    })
-                    .element(),
-                )
-                .expanded()
-                .contained()
-                .with_background_color(Color::white())
-                .into_any()
-        })
-    }
-}
-
-mod theme {
-    pub struct Toggleable<T> {
-        pub inactive: T,
-        pub active: T,
-    }
-
-    impl<T> Toggleable<T> {
-        pub fn style_for(&self, active: bool) -> &T {
-            if active {
-                &self.active
-            } else {
-                &self.inactive
-            }
-        }
-    }
-}
-
-// Component creation:
-mod toggleable_button {
-    use gpui::{
-        elements::{ContainerStyle, LabelStyle, StatefulComponent},
-        scene::MouseClick,
-        EventContext, View,
-    };
-
-    use crate::{button_component::Button, theme::Toggleable};
-
-    pub struct ToggleableButton<V: View> {
-        active: bool,
-        style: Option<Toggleable<ContainerStyle>>,
-        button: Button<V>,
-    }
-
-    impl<V: View> ToggleableButton<V> {
-        pub fn new<F>(active: bool, on_click: F) -> Self
-        where
-            F: Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
-        {
-            Self {
-                active,
-                button: Button::new(on_click),
-                style: None,
-            }
-        }
-
-        pub fn with_text(self, text: &str, style: impl Into<LabelStyle>) -> ToggleableButton<V> {
-            ToggleableButton {
-                active: self.active,
-                style: self.style,
-                button: self.button.with_text(text, style),
-            }
-        }
-
-        pub fn with_style(self, style: Toggleable<ContainerStyle>) -> ToggleableButton<V> {
-            ToggleableButton {
-                active: self.active,
-                style: Some(style),
-                button: self.button,
-            }
-        }
-    }
-
-    impl<V: View> StatefulComponent<V> for ToggleableButton<V> {
-        fn render(self, v: &mut V, cx: &mut gpui::ViewContext<V>) -> gpui::AnyElement<V> {
-            let button = if let Some(style) = self.style {
-                self.button.with_style(*style.style_for(self.active))
-            } else {
-                self.button
-            };
-            button.render(v, cx)
-        }
-    }
-}
-
-mod button_component {
-
-    use gpui::{
-        elements::{ContainerStyle, Label, LabelStyle, MouseEventHandler, StatefulComponent},
-        platform::MouseButton,
-        scene::MouseClick,
-        AnyElement, Element, EventContext, TypeTag, View, ViewContext,
-    };
-
-    type ClickHandler<V> = Box<dyn Fn(MouseClick, &mut V, &mut EventContext<V>)>;
-
-    pub struct Button<V: View> {
-        click_handler: ClickHandler<V>,
-        tag: TypeTag,
-        contents: Option<AnyElement<V>>,
-        style: Option<ContainerStyle>,
-    }
-
-    impl<V: View> Button<V> {
-        pub fn new<F: Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static>(handler: F) -> Self {
-            Self {
-                click_handler: Box::new(handler),
-                tag: TypeTag::new::<F>(),
-                style: None,
-                contents: None,
-            }
-        }
-
-        pub fn with_text(mut self, text: &str, style: impl Into<LabelStyle>) -> Self {
-            self.contents = Some(Label::new(text.to_string(), style).into_any());
-            self
-        }
-
-        pub fn _with_contents<E: Element<V>>(mut self, contents: E) -> Self {
-            self.contents = Some(contents.into_any());
-            self
-        }
-
-        pub fn with_style(mut self, style: ContainerStyle) -> Self {
-            self.style = Some(style);
-            self
-        }
-    }
-
-    impl<V: View> StatefulComponent<V> for Button<V> {
-        fn render(self, _: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V> {
-            let click_handler = self.click_handler;
-
-            let result = MouseEventHandler::new_dynamic(self.tag, 0, cx, |_, _| {
-                self.contents
-                    .unwrap_or_else(|| gpui::elements::Empty::new().into_any())
-            })
-            .on_click(MouseButton::Left, move |click, v, cx| {
-                click_handler(click, v, cx);
-            })
-            .contained();
-
-            let result = if let Some(style) = self.style {
-                result.with_style(style)
-            } else {
-                result
-            };
-
-            result.into_any()
-        }
-    }
-}

crates/gpui/examples/corner_radii.rs 🔗

@@ -1,154 +0,0 @@
-use gpui::{
-    color::Color, geometry::rect::RectF, scene::Shadow, AnyElement, App, Element, Entity, Quad,
-    View,
-};
-use log::LevelFilter;
-use pathfinder_geometry::vector::vec2f;
-use simplelog::SimpleLogger;
-
-fn main() {
-    SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
-
-    App::new(()).unwrap().run(|cx| {
-        cx.platform().activate(true);
-        cx.add_window(Default::default(), |_| CornersView);
-    });
-}
-
-struct CornersView;
-
-impl Entity for CornersView {
-    type Event = ();
-}
-
-impl View for CornersView {
-    fn ui_name() -> &'static str {
-        "CornersView"
-    }
-
-    fn render(&mut self, _: &mut gpui::ViewContext<Self>) -> AnyElement<CornersView> {
-        CornersElement.into_any()
-    }
-}
-
-struct CornersElement;
-
-impl<V: View> gpui::Element<V> for CornersElement {
-    type LayoutState = ();
-
-    type PaintState = ();
-
-    fn layout(
-        &mut self,
-        constraint: gpui::SizeConstraint,
-        _: &mut V,
-        _: &mut gpui::ViewContext<V>,
-    ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
-        (constraint.max, ())
-    }
-
-    fn paint(
-        &mut self,
-        bounds: pathfinder_geometry::rect::RectF,
-        _: pathfinder_geometry::rect::RectF,
-        _: &mut Self::LayoutState,
-        _: &mut V,
-        cx: &mut gpui::ViewContext<V>,
-    ) -> Self::PaintState {
-        cx.scene().push_quad(Quad {
-            bounds,
-            background: Some(Color::white()),
-            ..Default::default()
-        });
-
-        cx.scene().push_layer(None);
-
-        cx.scene().push_quad(Quad {
-            bounds: RectF::new(vec2f(100., 100.), vec2f(100., 100.)),
-            background: Some(Color::red()),
-            border: Default::default(),
-            corner_radii: gpui::scene::CornerRadii {
-                top_left: 20.,
-                ..Default::default()
-            },
-        });
-
-        cx.scene().push_quad(Quad {
-            bounds: RectF::new(vec2f(200., 100.), vec2f(100., 100.)),
-            background: Some(Color::green()),
-            border: Default::default(),
-            corner_radii: gpui::scene::CornerRadii {
-                top_right: 20.,
-                ..Default::default()
-            },
-        });
-
-        cx.scene().push_quad(Quad {
-            bounds: RectF::new(vec2f(100., 200.), vec2f(100., 100.)),
-            background: Some(Color::blue()),
-            border: Default::default(),
-            corner_radii: gpui::scene::CornerRadii {
-                bottom_left: 20.,
-                ..Default::default()
-            },
-        });
-
-        cx.scene().push_quad(Quad {
-            bounds: RectF::new(vec2f(200., 200.), vec2f(100., 100.)),
-            background: Some(Color::yellow()),
-            border: Default::default(),
-            corner_radii: gpui::scene::CornerRadii {
-                bottom_right: 20.,
-                ..Default::default()
-            },
-        });
-
-        cx.scene().push_shadow(Shadow {
-            bounds: RectF::new(vec2f(400., 100.), vec2f(100., 100.)),
-            corner_radii: gpui::scene::CornerRadii {
-                bottom_right: 20.,
-                ..Default::default()
-            },
-            sigma: 20.0,
-            color: Color::black(),
-        });
-
-        cx.scene().push_layer(None);
-        cx.scene().push_quad(Quad {
-            bounds: RectF::new(vec2f(400., 100.), vec2f(100., 100.)),
-            background: Some(Color::red()),
-            border: Default::default(),
-            corner_radii: gpui::scene::CornerRadii {
-                bottom_right: 20.,
-                ..Default::default()
-            },
-        });
-
-        cx.scene().pop_layer();
-        cx.scene().pop_layer();
-    }
-
-    fn rect_for_text_range(
-        &self,
-        _: std::ops::Range<usize>,
-        _: pathfinder_geometry::rect::RectF,
-        _: pathfinder_geometry::rect::RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        _: &V,
-        _: &gpui::ViewContext<V>,
-    ) -> Option<pathfinder_geometry::rect::RectF> {
-        unimplemented!()
-    }
-
-    fn debug(
-        &self,
-        _: pathfinder_geometry::rect::RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        _: &V,
-        _: &gpui::ViewContext<V>,
-    ) -> serde_json::Value {
-        unimplemented!()
-    }
-}

crates/gpui/examples/text.rs 🔗

@@ -1,81 +0,0 @@
-use gpui::{
-    color::Color,
-    elements::Text,
-    fonts::{HighlightStyle, TextStyle},
-    platform::{CursorStyle, MouseButton},
-    AnyElement, CursorRegion, Element, MouseRegion,
-};
-use log::LevelFilter;
-use simplelog::SimpleLogger;
-
-fn main() {
-    SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
-
-    gpui::App::new(()).unwrap().run(|cx| {
-        cx.platform().activate(true);
-        cx.add_window(Default::default(), |_| TextView);
-    });
-}
-
-struct TextView;
-
-impl gpui::Entity for TextView {
-    type Event = ();
-}
-
-impl gpui::View for TextView {
-    fn ui_name() -> &'static str {
-        "View"
-    }
-
-    fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> AnyElement<TextView> {
-        let font_size = 12.;
-        let family = cx
-            .font_cache
-            .load_family(&["Monaco"], &Default::default())
-            .unwrap();
-        let font_id = cx
-            .font_cache
-            .select_font(family, &Default::default())
-            .unwrap();
-        let view_id = cx.view_id();
-
-        let underline = HighlightStyle {
-            underline: Some(gpui::fonts::Underline {
-                thickness: 1.0.into(),
-                ..Default::default()
-            }),
-            ..Default::default()
-        };
-
-        Text::new(
-            "The text:\nHello, beautiful world, hello!",
-            TextStyle {
-                font_id,
-                font_size,
-                color: Color::red(),
-                font_family_name: "".into(),
-                font_family_id: family,
-                underline: Default::default(),
-                font_properties: Default::default(),
-                soft_wrap: false,
-            },
-        )
-        .with_highlights(vec![(17..26, underline), (34..40, underline)])
-        .with_custom_runs(vec![(17..26), (34..40)], move |ix, bounds, cx| {
-            cx.scene().push_cursor_region(CursorRegion {
-                bounds,
-                style: CursorStyle::PointingHand,
-            });
-            cx.scene().push_mouse_region(
-                MouseRegion::new::<Self>(view_id, ix, bounds).on_click::<Self, _>(
-                    MouseButton::Left,
-                    move |_, _, _| {
-                        eprintln!("clicked link {ix}");
-                    },
-                ),
-            );
-        })
-        .into_any()
-    }
-}

crates/gpui/src/app.rs 🔗

@@ -1,624 +1,312 @@
-pub mod action;
-mod callback_collection;
-mod menu;
-pub(crate) mod ref_counts;
+mod async_context;
+mod entity_map;
+mod model_context;
 #[cfg(any(test, feature = "test-support"))]
-pub mod test_app_context;
-pub(crate) mod window;
-mod window_input_handler;
+mod test_context;
+
+pub use async_context::*;
+use derive_more::{Deref, DerefMut};
+pub use entity_map::*;
+pub use model_context::*;
+use refineable::Refineable;
+use smol::future::FutureExt;
+#[cfg(any(test, feature = "test-support"))]
+pub use test_context::*;
+use time::UtcOffset;
 
 use crate::{
-    elements::{AnyElement, AnyRootElement, RootElement},
-    executor::{self, Task},
-    image_cache::ImageCache,
-    json,
-    keymap_matcher::{self, Binding, KeymapContext, KeymapMatcher, Keystroke, MatchResult},
-    platform::{
-        self, FontSystem, KeyDownEvent, KeyUpEvent, ModifiersChangedEvent, MouseButton,
-        PathPromptOptions, Platform, PromptLevel, WindowBounds, WindowOptions,
-    },
-    util::post_inc,
-    window::{Window, WindowContext},
-    AssetCache, AssetSource, ClipboardItem, FontCache, MouseRegionId,
+    current_platform, image_cache::ImageCache, init_app_menus, Action, ActionRegistry, Any,
+    AnyView, AnyWindowHandle, AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context,
+    DispatchPhase, DisplayId, Entity, EventEmitter, ForegroundExecutor, KeyBinding, Keymap,
+    Keystroke, LayoutId, Menu, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, Render,
+    SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement,
+    TextSystem, View, ViewContext, Window, WindowContext, WindowHandle, WindowId,
 };
-pub use action::*;
-use anyhow::{anyhow, Context, Result};
-use callback_collection::CallbackCollection;
-use collections::{hash_map::Entry, BTreeMap, HashMap, HashSet, VecDeque};
-use derive_more::Deref;
-pub use menu::*;
+use anyhow::{anyhow, Result};
+use collections::{FxHashMap, FxHashSet, VecDeque};
+use futures::{channel::oneshot, future::LocalBoxFuture, Future};
 use parking_lot::Mutex;
-use pathfinder_geometry::rect::RectF;
-use platform::Event;
-use postage::oneshot;
-#[cfg(any(test, feature = "test-support"))]
-use ref_counts::LeakDetector;
-use ref_counts::RefCounts;
-use smallvec::SmallVec;
-use smol::prelude::*;
+use slotmap::SlotMap;
 use std::{
-    any::{type_name, Any, TypeId},
-    cell::RefCell,
-    fmt::{self, Debug},
-    hash::{Hash, Hasher},
+    any::{type_name, TypeId},
+    cell::{Ref, RefCell, RefMut},
     marker::PhantomData,
     mem,
-    ops::{Deref, DerefMut, Range},
+    ops::{Deref, DerefMut},
     path::{Path, PathBuf},
-    pin::Pin,
-    rc::{self, Rc},
-    sync::{Arc, Weak},
+    rc::{Rc, Weak},
+    sync::{atomic::Ordering::SeqCst, Arc},
     time::Duration,
 };
-#[cfg(any(test, feature = "test-support"))]
-pub use test_app_context::{ContextHandle, TestAppContext};
 use util::{
     http::{self, HttpClient},
     ResultExt,
 };
-use uuid::Uuid;
-pub use window::MeasureParams;
-use window_input_handler::WindowInputHandler;
 
-pub trait Entity: 'static {
-    type Event;
-
-    fn release(&mut self, _: &mut AppContext) {}
-    fn app_will_quit(
-        &mut self,
-        _: &mut AppContext,
-    ) -> Option<Pin<Box<dyn 'static + Future<Output = ()>>>> {
-        None
-    }
+/// Temporary(?) wrapper around RefCell<AppContext> to help us debug any double borrows.
+/// Strongly consider removing after stabilization.
+pub struct AppCell {
+    app: RefCell<AppContext>,
 }
 
-pub trait View: Entity + Sized {
-    fn render(&mut self, cx: &mut ViewContext<'_, '_, Self>) -> AnyElement<Self>;
-    fn focus_in(&mut self, _: AnyViewHandle, _: &mut ViewContext<Self>) {}
-    fn focus_out(&mut self, _: AnyViewHandle, _: &mut ViewContext<Self>) {}
-    fn ui_name() -> &'static str {
-        type_name::<Self>()
-    }
-    fn key_down(&mut self, _: &KeyDownEvent, _: &mut ViewContext<Self>) -> bool {
-        false
-    }
-    fn key_up(&mut self, _: &KeyUpEvent, _: &mut ViewContext<Self>) -> bool {
-        false
-    }
-    fn modifiers_changed(&mut self, _: &ModifiersChangedEvent, _: &mut ViewContext<Self>) -> bool {
-        false
-    }
-
-    fn update_keymap_context(&self, keymap: &mut keymap_matcher::KeymapContext, _: &AppContext) {
-        Self::reset_to_default_keymap_context(keymap);
+impl AppCell {
+    #[track_caller]
+    pub fn borrow(&self) -> AppRef {
+        if option_env!("TRACK_THREAD_BORROWS").is_some() {
+            let thread_id = std::thread::current().id();
+            eprintln!("borrowed {thread_id:?}");
+        }
+        AppRef(self.app.borrow())
     }
 
-    fn reset_to_default_keymap_context(keymap: &mut keymap_matcher::KeymapContext) {
-        keymap.clear();
-        keymap.add_identifier(Self::ui_name());
+    #[track_caller]
+    pub fn borrow_mut(&self) -> AppRefMut {
+        if option_env!("TRACK_THREAD_BORROWS").is_some() {
+            let thread_id = std::thread::current().id();
+            eprintln!("borrowed {thread_id:?}");
+        }
+        AppRefMut(self.app.borrow_mut())
     }
+}
 
-    fn debug_json(&self, _: &AppContext) -> serde_json::Value {
-        serde_json::Value::Null
-    }
+#[derive(Deref, DerefMut)]
+pub struct AppRef<'a>(Ref<'a, AppContext>);
 
-    fn text_for_range(&self, _: Range<usize>, _: &AppContext) -> Option<String> {
-        None
-    }
-    fn selected_text_range(&self, _: &AppContext) -> Option<Range<usize>> {
-        None
-    }
-    fn marked_text_range(&self, _: &AppContext) -> Option<Range<usize>> {
-        None
-    }
-    fn unmark_text(&mut self, _: &mut ViewContext<Self>) {}
-    fn replace_text_in_range(
-        &mut self,
-        _: Option<Range<usize>>,
-        _: &str,
-        _: &mut ViewContext<Self>,
-    ) {
-    }
-    fn replace_and_mark_text_in_range(
-        &mut self,
-        _: Option<Range<usize>>,
-        _: &str,
-        _: Option<Range<usize>>,
-        _: &mut ViewContext<Self>,
-    ) {
+impl<'a> Drop for AppRef<'a> {
+    fn drop(&mut self) {
+        if option_env!("TRACK_THREAD_BORROWS").is_some() {
+            let thread_id = std::thread::current().id();
+            eprintln!("dropped borrow from {thread_id:?}");
+        }
     }
 }
 
-pub trait BorrowAppContext {
-    fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T;
-    fn update<T, F: FnOnce(&mut AppContext) -> T>(&mut self, f: F) -> T;
-}
-
-pub trait BorrowWindowContext {
-    type Result<T>;
+#[derive(Deref, DerefMut)]
+pub struct AppRefMut<'a>(RefMut<'a, AppContext>);
 
-    fn read_window<T, F>(&self, window: AnyWindowHandle, f: F) -> Self::Result<T>
-    where
-        F: FnOnce(&WindowContext) -> T;
-    fn read_window_optional<T, F>(&self, window: AnyWindowHandle, f: F) -> Option<T>
-    where
-        F: FnOnce(&WindowContext) -> Option<T>;
-    fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Self::Result<T>
-    where
-        F: FnOnce(&mut WindowContext) -> T;
-    fn update_window_optional<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Option<T>
-    where
-        F: FnOnce(&mut WindowContext) -> Option<T>;
+impl<'a> Drop for AppRefMut<'a> {
+    fn drop(&mut self) {
+        if option_env!("TRACK_THREAD_BORROWS").is_some() {
+            let thread_id = std::thread::current().id();
+            eprintln!("dropped {thread_id:?}");
+        }
+    }
 }
 
-#[derive(Clone)]
-pub struct App(Rc<RefCell<AppContext>>);
+pub struct App(Rc<AppCell>);
 
+/// Represents an application before it is fully launched. Once your app is
+/// configured, you'll start the app with `App::run`.
 impl App {
-    pub fn new(asset_source: impl AssetSource) -> Result<Self> {
-        let platform = platform::current::platform();
-        let foreground = Rc::new(executor::Foreground::platform(platform.dispatcher())?);
-        let foreground_platform = platform::current::foreground_platform(foreground.clone());
-        let http_client = http::client();
-        let app = Self(Rc::new(RefCell::new(AppContext::new(
-            foreground,
-            Arc::new(executor::Background::new()),
-            platform.clone(),
-            foreground_platform.clone(),
-            Arc::new(FontCache::new(platform.fonts())),
-            http_client,
-            Default::default(),
+    /// Builds an app with the given asset source.
+    pub fn production(asset_source: Arc<dyn AssetSource>) -> Self {
+        Self(AppContext::new(
+            current_platform(),
             asset_source,
-        ))));
-
-        foreground_platform.on_event(Box::new({
-            let cx = app.0.clone();
-            move |event| {
-                if let Event::KeyDown(KeyDownEvent { keystroke, .. }) = &event {
-                    // Allow system menu "cmd-?" shortcut to be overridden
-                    if keystroke.cmd
-                        && !keystroke.shift
-                        && !keystroke.alt
-                        && !keystroke.function
-                        && keystroke.key == "?"
-                    {
-                        if cx
-                            .borrow_mut()
-                            .update_active_window(|cx| cx.dispatch_keystroke(keystroke))
-                            .unwrap_or(false)
-                        {
-                            return true;
-                        }
-                    }
-                }
-                false
-            }
-        }));
-        foreground_platform.on_quit(Box::new({
-            let cx = app.0.clone();
-            move || {
-                cx.borrow_mut().quit();
-            }
-        }));
-        setup_menu_handlers(foreground_platform.as_ref(), &app);
-
-        app.0.borrow_mut().weak_self = Some(Rc::downgrade(&app.0));
-        Ok(app)
-    }
-
-    pub fn background(&self) -> Arc<executor::Background> {
-        self.0.borrow().background().clone()
-    }
-
-    pub fn on_become_active<F>(self, mut callback: F) -> Self
-    where
-        F: 'static + FnMut(&mut AppContext),
-    {
-        let cx = self.0.clone();
-        self.0
-            .borrow_mut()
-            .foreground_platform
-            .on_become_active(Box::new(move || callback(&mut *cx.borrow_mut())));
-        self
-    }
-
-    pub fn on_resign_active<F>(self, mut callback: F) -> Self
-    where
-        F: 'static + FnMut(&mut AppContext),
-    {
-        let cx = self.0.clone();
-        self.0
-            .borrow_mut()
-            .foreground_platform
-            .on_resign_active(Box::new(move || callback(&mut *cx.borrow_mut())));
-        self
-    }
-
-    pub fn on_quit<F>(&mut self, mut callback: F) -> &mut Self
-    where
-        F: 'static + FnMut(&mut AppContext),
-    {
-        let cx = self.0.clone();
-        self.0
-            .borrow_mut()
-            .foreground_platform
-            .on_quit(Box::new(move || callback(&mut *cx.borrow_mut())));
-        self
-    }
-
-    /// Handle the application being re-activated when no windows are open.
-    pub fn on_reopen<F>(&mut self, mut callback: F) -> &mut Self
-    where
-        F: 'static + FnMut(&mut AppContext),
-    {
-        let cx = self.0.clone();
-        self.0
-            .borrow_mut()
-            .foreground_platform
-            .on_reopen(Box::new(move || callback(&mut *cx.borrow_mut())));
-        self
-    }
-
-    pub fn on_event<F>(&mut self, mut callback: F) -> &mut Self
-    where
-        F: 'static + FnMut(Event, &mut AppContext) -> bool,
-    {
-        let cx = self.0.clone();
-        self.0
-            .borrow_mut()
-            .foreground_platform
-            .on_event(Box::new(move |event| {
-                callback(event, &mut *cx.borrow_mut())
-            }));
-        self
-    }
-
-    pub fn on_open_urls<F>(&mut self, mut callback: F) -> &mut Self
-    where
-        F: 'static + FnMut(Vec<String>, &mut AppContext),
-    {
-        let cx = self.0.clone();
-        self.0
-            .borrow_mut()
-            .foreground_platform
-            .on_open_urls(Box::new(move |urls| callback(urls, &mut *cx.borrow_mut())));
-        self
+            http::client(),
+        ))
     }
 
+    /// Start the application. The provided callback will be called once the
+    /// app is fully launched.
     pub fn run<F>(self, on_finish_launching: F)
     where
         F: 'static + FnOnce(&mut AppContext),
     {
-        let platform = self.0.borrow().foreground_platform.clone();
+        let this = self.0.clone();
+        let platform = self.0.borrow().platform.clone();
         platform.run(Box::new(move || {
-            let mut cx = self.0.borrow_mut();
-            let cx = &mut *cx;
-            crate::views::init(cx);
+            let cx = &mut *this.borrow_mut();
             on_finish_launching(cx);
-        }))
-    }
-
-    pub fn platform(&self) -> Arc<dyn Platform> {
-        self.0.borrow().platform.clone()
-    }
-
-    pub fn font_cache(&self) -> Arc<FontCache> {
-        self.0.borrow().font_cache.clone()
-    }
-
-    fn update<T, F: FnOnce(&mut AppContext) -> T>(&mut self, callback: F) -> T {
-        let mut state = self.0.borrow_mut();
-        let result = state.update(callback);
-        state.pending_notifications.clear();
-        result
-    }
-
-    fn update_window<T, F>(&mut self, window: AnyWindowHandle, callback: F) -> Option<T>
-    where
-        F: FnOnce(&mut WindowContext) -> T,
-    {
-        let mut state = self.0.borrow_mut();
-        let result = state.update_window(window, callback);
-        state.pending_notifications.clear();
-        result
-    }
-}
-
-#[derive(Clone)]
-pub struct AsyncAppContext(Rc<RefCell<AppContext>>);
-
-impl AsyncAppContext {
-    pub fn spawn<F, Fut, T>(&self, f: F) -> Task<T>
-    where
-        F: FnOnce(AsyncAppContext) -> Fut,
-        Fut: 'static + Future<Output = T>,
-        T: 'static,
-    {
-        self.0.borrow().foreground.spawn(f(self.clone()))
-    }
-
-    pub fn read<T, F: FnOnce(&AppContext) -> T>(&self, callback: F) -> T {
-        callback(&*self.0.borrow())
-    }
-
-    pub fn update<T, F: FnOnce(&mut AppContext) -> T>(&mut self, callback: F) -> T {
-        self.0.borrow_mut().update(callback)
-    }
-
-    pub fn windows(&self) -> Vec<AnyWindowHandle> {
-        self.0.borrow().windows().collect()
+        }));
     }
 
-    pub fn add_model<T, F>(&mut self, build_model: F) -> ModelHandle<T>
+    /// Register a handler to be invoked when the platform instructs the application
+    /// to open one or more URLs.
+    pub fn on_open_urls<F>(&self, mut callback: F) -> &Self
     where
-        T: Entity,
-        F: FnOnce(&mut ModelContext<T>) -> T,
+        F: 'static + FnMut(Vec<String>, &mut AppContext),
     {
-        self.update(|cx| cx.add_model(build_model))
+        let this = Rc::downgrade(&self.0);
+        self.0.borrow().platform.on_open_urls(Box::new(move |urls| {
+            if let Some(app) = this.upgrade() {
+                callback(urls, &mut app.borrow_mut());
+            }
+        }));
+        self
     }
 
-    pub fn add_window<T, F>(
-        &mut self,
-        window_options: WindowOptions,
-        build_root_view: F,
-    ) -> WindowHandle<T>
+    pub fn on_reopen<F>(&self, mut callback: F) -> &Self
     where
-        T: View,
-        F: FnOnce(&mut ViewContext<T>) -> T,
+        F: 'static + FnMut(&mut AppContext),
     {
-        self.update(|cx| cx.add_window(window_options, build_root_view))
-    }
-
-    pub fn platform(&self) -> Arc<dyn Platform> {
-        self.0.borrow().platform().clone()
+        let this = Rc::downgrade(&self.0);
+        self.0.borrow_mut().platform.on_reopen(Box::new(move || {
+            if let Some(app) = this.upgrade() {
+                callback(&mut app.borrow_mut());
+            }
+        }));
+        self
     }
 
-    pub fn foreground(&self) -> Rc<executor::Foreground> {
-        self.0.borrow().foreground.clone()
+    pub fn metadata(&self) -> AppMetadata {
+        self.0.borrow().app_metadata.clone()
     }
 
-    pub fn background(&self) -> Arc<executor::Background> {
-        self.0.borrow().background.clone()
+    pub fn background_executor(&self) -> BackgroundExecutor {
+        self.0.borrow().background_executor.clone()
     }
-}
 
-impl BorrowAppContext for AsyncAppContext {
-    fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
-        self.0.borrow().read_with(f)
+    pub fn foreground_executor(&self) -> ForegroundExecutor {
+        self.0.borrow().foreground_executor.clone()
     }
 
-    fn update<T, F: FnOnce(&mut AppContext) -> T>(&mut self, f: F) -> T {
-        self.0.borrow_mut().update(f)
+    pub fn text_system(&self) -> Arc<TextSystem> {
+        self.0.borrow().text_system.clone()
     }
 }
 
-impl BorrowWindowContext for AsyncAppContext {
-    type Result<T> = Option<T>;
-
-    fn read_window<T, F>(&self, window: AnyWindowHandle, f: F) -> Self::Result<T>
-    where
-        F: FnOnce(&WindowContext) -> T,
-    {
-        self.0.borrow().read_with(|cx| cx.read_window(window, f))
-    }
-
-    fn read_window_optional<T, F>(&self, window: AnyWindowHandle, f: F) -> Option<T>
-    where
-        F: FnOnce(&WindowContext) -> Option<T>,
-    {
-        self.0
-            .borrow_mut()
-            .update(|cx| cx.read_window_optional(window, f))
-    }
-
-    fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Self::Result<T>
-    where
-        F: FnOnce(&mut WindowContext) -> T,
-    {
-        self.0.borrow_mut().update(|cx| cx.update_window(window, f))
-    }
-
-    fn update_window_optional<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Option<T>
-    where
-        F: FnOnce(&mut WindowContext) -> Option<T>,
-    {
-        self.0
-            .borrow_mut()
-            .update(|cx| cx.update_window_optional(window, f))
-    }
-}
+pub(crate) type FrameCallback = Box<dyn FnOnce(&mut AppContext)>;
+type Handler = Box<dyn FnMut(&mut AppContext) -> bool + 'static>;
+type Listener = Box<dyn FnMut(&dyn Any, &mut AppContext) -> bool + 'static>;
+type KeystrokeObserver = Box<dyn FnMut(&KeystrokeEvent, &mut WindowContext) + 'static>;
+type QuitHandler = Box<dyn FnOnce(&mut AppContext) -> LocalBoxFuture<'static, ()> + 'static>;
+type ReleaseListener = Box<dyn FnOnce(&mut dyn Any, &mut AppContext) + 'static>;
+type NewViewListener = Box<dyn FnMut(AnyView, &mut WindowContext) + 'static>;
 
-type ActionCallback = dyn FnMut(&mut dyn AnyView, &dyn Action, &mut WindowContext, usize);
-type GlobalActionCallback = dyn FnMut(&dyn Action, &mut AppContext);
-
-type SubscriptionCallback = Box<dyn FnMut(&dyn Any, &mut AppContext) -> bool>;
-type GlobalSubscriptionCallback = Box<dyn FnMut(&dyn Any, &mut AppContext)>;
-type ObservationCallback = Box<dyn FnMut(&mut AppContext) -> bool>;
-type GlobalObservationCallback = Box<dyn FnMut(&mut AppContext)>;
-type FocusObservationCallback = Box<dyn FnMut(bool, &mut WindowContext) -> bool>;
-type ReleaseObservationCallback = Box<dyn FnMut(&dyn Any, &mut AppContext)>;
-type ActionObservationCallback = Box<dyn FnMut(TypeId, &mut AppContext)>;
-type WindowActivationCallback = Box<dyn FnMut(bool, &mut WindowContext) -> bool>;
-type WindowFullscreenCallback = Box<dyn FnMut(bool, &mut WindowContext) -> bool>;
-type WindowBoundsCallback = Box<dyn FnMut(WindowBounds, Uuid, &mut WindowContext) -> bool>;
-type KeystrokeCallback =
-    Box<dyn FnMut(&Keystroke, &MatchResult, Option<&Box<dyn Action>>, &mut WindowContext) -> bool>;
-type ActiveLabeledTasksCallback = Box<dyn FnMut(&mut AppContext) -> bool>;
-type DeserializeActionCallback = fn(json: serde_json::Value) -> anyhow::Result<Box<dyn Action>>;
-type WindowShouldCloseSubscriptionCallback = Box<dyn FnMut(&mut AppContext) -> bool>;
+// struct FrameConsumer {
+//     next_frame_callbacks: Vec<FrameCallback>,
+//     task: Task<()>,
+//     display_linker
+// }
 
 pub struct AppContext {
-    models: HashMap<usize, Box<dyn AnyModel>>,
-    views: HashMap<(AnyWindowHandle, usize), Box<dyn AnyView>>,
-    views_metadata: HashMap<(AnyWindowHandle, usize), ViewMetadata>,
-    windows: HashMap<AnyWindowHandle, Window>,
-    globals: HashMap<TypeId, Box<dyn Any>>,
-    element_states: HashMap<ElementStateId, Box<dyn Any>>,
-    background: Arc<executor::Background>,
-    ref_counts: Arc<Mutex<RefCounts>>,
-
-    weak_self: Option<rc::Weak<RefCell<Self>>>,
-    platform: Arc<dyn Platform>,
-    foreground_platform: Rc<dyn platform::ForegroundPlatform>,
-    pub asset_cache: Arc<AssetCache>,
-    font_system: Arc<dyn FontSystem>,
-    pub font_cache: Arc<FontCache>,
-    pub image_cache: Arc<ImageCache>,
-    action_deserializers: HashMap<&'static str, (TypeId, DeserializeActionCallback)>,
-    capture_actions: HashMap<TypeId, HashMap<TypeId, Vec<Box<ActionCallback>>>>,
-    // Entity Types -> { Action Types -> Action Handlers }
-    actions: HashMap<TypeId, HashMap<TypeId, Vec<Box<ActionCallback>>>>,
-    // Action Types -> Action Handlers
-    global_actions: HashMap<TypeId, Box<GlobalActionCallback>>,
-    keystroke_matcher: KeymapMatcher,
-    next_id: usize,
-    // next_window: AnyWindowHandle,
-    next_subscription_id: usize,
-    frame_count: usize,
-
-    subscriptions: CallbackCollection<usize, SubscriptionCallback>,
-    global_subscriptions: CallbackCollection<TypeId, GlobalSubscriptionCallback>,
-    observations: CallbackCollection<usize, ObservationCallback>,
-    global_observations: CallbackCollection<TypeId, GlobalObservationCallback>,
-    focus_observations: CallbackCollection<usize, FocusObservationCallback>,
-    release_observations: CallbackCollection<usize, ReleaseObservationCallback>,
-    action_dispatch_observations: CallbackCollection<(), ActionObservationCallback>,
-    window_activation_observations: CallbackCollection<AnyWindowHandle, WindowActivationCallback>,
-    window_fullscreen_observations: CallbackCollection<AnyWindowHandle, WindowFullscreenCallback>,
-    window_bounds_observations: CallbackCollection<AnyWindowHandle, WindowBoundsCallback>,
-    keystroke_observations: CallbackCollection<AnyWindowHandle, KeystrokeCallback>,
-    active_labeled_task_observations: CallbackCollection<(), ActiveLabeledTasksCallback>,
-
-    foreground: Rc<executor::Foreground>,
-    pending_effects: VecDeque<Effect>,
-    pending_notifications: HashSet<usize>,
-    pending_global_notifications: HashSet<TypeId>,
-    pending_flushes: usize,
+    pub(crate) this: Weak<AppCell>,
+    pub(crate) platform: Rc<dyn Platform>,
+    app_metadata: AppMetadata,
+    text_system: Arc<TextSystem>,
     flushing_effects: bool,
-    halt_action_dispatch: bool,
-    next_labeled_task_id: usize,
-    active_labeled_tasks: BTreeMap<usize, &'static str>,
+    pending_updates: usize,
+    pub(crate) actions: Rc<ActionRegistry>,
+    pub(crate) active_drag: Option<AnyDrag>,
+    pub(crate) active_tooltip: Option<AnyTooltip>,
+    pub(crate) next_frame_callbacks: FxHashMap<DisplayId, Vec<FrameCallback>>,
+    pub(crate) frame_consumers: FxHashMap<DisplayId, Task<()>>,
+    pub(crate) background_executor: BackgroundExecutor,
+    pub(crate) foreground_executor: ForegroundExecutor,
+    pub(crate) svg_renderer: SvgRenderer,
+    asset_source: Arc<dyn AssetSource>,
+    pub(crate) image_cache: ImageCache,
+    pub(crate) text_style_stack: Vec<TextStyleRefinement>,
+    pub(crate) globals_by_type: FxHashMap<TypeId, Box<dyn Any>>,
+    pub(crate) entities: EntityMap,
+    pub(crate) new_view_observers: SubscriberSet<TypeId, NewViewListener>,
+    pub(crate) windows: SlotMap<WindowId, Option<Window>>,
+    pub(crate) keymap: Arc<Mutex<Keymap>>,
+    pub(crate) global_action_listeners:
+        FxHashMap<TypeId, Vec<Rc<dyn Fn(&dyn Any, DispatchPhase, &mut Self)>>>,
+    pending_effects: VecDeque<Effect>,
+    pub(crate) pending_notifications: FxHashSet<EntityId>,
+    pub(crate) pending_global_notifications: FxHashSet<TypeId>,
+    pub(crate) observers: SubscriberSet<EntityId, Handler>,
+    // TypeId is the type of the event that the listener callback expects
+    pub(crate) event_listeners: SubscriberSet<EntityId, (TypeId, Listener)>,
+    pub(crate) keystroke_observers: SubscriberSet<(), KeystrokeObserver>,
+    pub(crate) release_listeners: SubscriberSet<EntityId, ReleaseListener>,
+    pub(crate) global_observers: SubscriberSet<TypeId, Handler>,
+    pub(crate) quit_observers: SubscriberSet<(), QuitHandler>,
+    pub(crate) layout_id_buffer: Vec<LayoutId>, // We recycle this memory across layout requests.
+    pub(crate) propagate_event: bool,
 }
 
 impl AppContext {
-    fn new(
-        foreground: Rc<executor::Foreground>,
-        background: Arc<executor::Background>,
-        platform: Arc<dyn platform::Platform>,
-        foreground_platform: Rc<dyn platform::ForegroundPlatform>,
-        font_cache: Arc<FontCache>,
+    pub(crate) fn new(
+        platform: Rc<dyn Platform>,
+        asset_source: Arc<dyn AssetSource>,
         http_client: Arc<dyn HttpClient>,
-        ref_counts: RefCounts,
-        asset_source: impl AssetSource,
-    ) -> Self {
-        Self {
-            models: Default::default(),
-            views: Default::default(),
-            views_metadata: Default::default(),
-            windows: Default::default(),
-            globals: Default::default(),
-            element_states: Default::default(),
-            ref_counts: Arc::new(Mutex::new(ref_counts)),
-            background,
-
-            weak_self: None,
-            font_system: platform.fonts(),
-            platform,
-            foreground_platform,
-            font_cache,
-            image_cache: Arc::new(ImageCache::new(http_client)),
-            asset_cache: Arc::new(AssetCache::new(asset_source)),
-            action_deserializers: Default::default(),
-            capture_actions: Default::default(),
-            actions: Default::default(),
-            global_actions: Default::default(),
-            keystroke_matcher: KeymapMatcher::default(),
-            next_id: 0,
-            next_subscription_id: 0,
-            frame_count: 0,
-            subscriptions: Default::default(),
-            global_subscriptions: Default::default(),
-            observations: Default::default(),
-            focus_observations: Default::default(),
-            release_observations: Default::default(),
-            global_observations: Default::default(),
-            window_activation_observations: Default::default(),
-            window_fullscreen_observations: Default::default(),
-            window_bounds_observations: Default::default(),
-            keystroke_observations: Default::default(),
-            action_dispatch_observations: Default::default(),
-            active_labeled_task_observations: Default::default(),
-            foreground,
-            pending_effects: VecDeque::new(),
-            pending_notifications: Default::default(),
-            pending_global_notifications: Default::default(),
-            pending_flushes: 0,
-            flushing_effects: false,
-            halt_action_dispatch: false,
-            next_labeled_task_id: 0,
-            active_labeled_tasks: Default::default(),
-        }
-    }
-
-    pub fn background(&self) -> &Arc<executor::Background> {
-        &self.background
-    }
+    ) -> Rc<AppCell> {
+        let executor = platform.background_executor();
+        let foreground_executor = platform.foreground_executor();
+        assert!(
+            executor.is_main_thread(),
+            "must construct App on main thread"
+        );
 
-    pub fn font_cache(&self) -> &Arc<FontCache> {
-        &self.font_cache
-    }
+        let text_system = Arc::new(TextSystem::new(platform.text_system()));
+        let entities = EntityMap::new();
 
-    pub fn platform(&self) -> &Arc<dyn Platform> {
-        &self.platform
-    }
+        let app_metadata = AppMetadata {
+            os_name: platform.os_name(),
+            os_version: platform.os_version().ok(),
+            app_version: platform.app_version().ok(),
+        };
 
-    pub fn has_global<T: 'static>(&self) -> bool {
-        self.globals.contains_key(&TypeId::of::<T>())
-    }
+        let app = Rc::new_cyclic(|this| AppCell {
+            app: RefCell::new(AppContext {
+                this: this.clone(),
+                platform: platform.clone(),
+                app_metadata,
+                text_system,
+                actions: Rc::new(ActionRegistry::default()),
+                flushing_effects: false,
+                pending_updates: 0,
+                active_drag: None,
+                active_tooltip: None,
+                next_frame_callbacks: FxHashMap::default(),
+                frame_consumers: FxHashMap::default(),
+                background_executor: executor,
+                foreground_executor,
+                svg_renderer: SvgRenderer::new(asset_source.clone()),
+                asset_source,
+                image_cache: ImageCache::new(http_client),
+                text_style_stack: Vec::new(),
+                globals_by_type: FxHashMap::default(),
+                entities,
+                new_view_observers: SubscriberSet::new(),
+                windows: SlotMap::with_key(),
+                keymap: Arc::new(Mutex::new(Keymap::default())),
+                global_action_listeners: FxHashMap::default(),
+                pending_effects: VecDeque::new(),
+                pending_notifications: FxHashSet::default(),
+                pending_global_notifications: FxHashSet::default(),
+                observers: SubscriberSet::new(),
+                event_listeners: SubscriberSet::new(),
+                release_listeners: SubscriberSet::new(),
+                keystroke_observers: SubscriberSet::new(),
+                global_observers: SubscriberSet::new(),
+                quit_observers: SubscriberSet::new(),
+                layout_id_buffer: Default::default(),
+                propagate_event: true,
+            }),
+        });
 
-    pub fn global<T: 'static>(&self) -> &T {
-        if let Some(global) = self.globals.get(&TypeId::of::<T>()) {
-            global.downcast_ref().unwrap()
-        } else {
-            panic!("no global has been added for {}", type_name::<T>());
-        }
-    }
+        init_app_menus(platform.as_ref(), &mut app.borrow_mut());
 
-    pub fn optional_global<T: 'static>(&self) -> Option<&T> {
-        if let Some(global) = self.globals.get(&TypeId::of::<T>()) {
-            Some(global.downcast_ref().unwrap())
-        } else {
-            None
-        }
-    }
+        platform.on_quit(Box::new({
+            let cx = app.clone();
+            move || {
+                cx.borrow_mut().shutdown();
+            }
+        }));
 
-    pub fn upgrade(&self) -> App {
-        App(self.weak_self.as_ref().unwrap().upgrade().unwrap())
+        app
     }
 
-    fn quit(&mut self) {
+    /// Quit the application gracefully. Handlers registered with `ModelContext::on_app_quit`
+    /// will be given 100ms to complete before exiting.
+    pub fn shutdown(&mut self) {
         let mut futures = Vec::new();
 
-        self.update(|cx| {
-            for model_id in cx.models.keys().copied().collect::<Vec<_>>() {
-                let mut model = cx.models.remove(&model_id).unwrap();
-                futures.extend(model.app_will_quit(cx));
-                cx.models.insert(model_id, model);
-            }
-
-            for view_id in cx.views.keys().copied().collect::<Vec<_>>() {
-                let mut view = cx.views.remove(&view_id).unwrap();
-                futures.extend(view.app_will_quit(cx));
-                cx.views.insert(view_id, view);
-            }
-        });
+        for observer in self.quit_observers.remove(&()) {
+            futures.push(observer(self));
+        }
 
         self.windows.clear();
         self.flush_effects();
 
         let futures = futures::future::join_all(futures);
         if self
-            .background
+            .background_executor
             .block_with_timeout(Duration::from_millis(100), futures)
             .is_err()
         {
@@ -626,6263 +314,951 @@ impl AppContext {
         }
     }
 
-    pub fn foreground(&self) -> &Rc<executor::Foreground> {
-        &self.foreground
+    pub fn quit(&mut self) {
+        self.platform.quit();
     }
 
-    pub fn deserialize_action(
-        &self,
-        name: &str,
-        argument: Option<serde_json::Value>,
-    ) -> Result<Box<dyn Action>> {
-        let callback = self
-            .action_deserializers
-            .get(name)
-            .ok_or_else(|| anyhow!("unknown action {}", name))?
-            .1;
-        callback(argument.unwrap_or_else(|| serde_json::Value::Object(Default::default())))
-            .with_context(|| format!("invalid data for action {}", name))
+    pub fn app_metadata(&self) -> AppMetadata {
+        self.app_metadata.clone()
     }
 
-    pub fn add_action<A, V, F, R>(&mut self, handler: F)
-    where
-        A: Action,
-        V: 'static,
-        F: 'static + FnMut(&mut V, &A, &mut ViewContext<V>) -> R,
-    {
-        self.add_action_internal(handler, false)
+    /// Schedules all windows in the application to be redrawn. This can be called
+    /// multiple times in an update cycle and still result in a single redraw.
+    pub fn refresh(&mut self) {
+        self.pending_effects.push_back(Effect::Refresh);
+    }
+    pub(crate) fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
+        self.pending_updates += 1;
+        let result = update(self);
+        if !self.flushing_effects && self.pending_updates == 1 {
+            self.flushing_effects = true;
+            self.flush_effects();
+            self.flushing_effects = false;
+        }
+        self.pending_updates -= 1;
+        result
     }
 
-    pub fn capture_action<A, V, F>(&mut self, handler: F)
+    pub fn observe<W, E>(
+        &mut self,
+        entity: &E,
+        mut on_notify: impl FnMut(E, &mut AppContext) + 'static,
+    ) -> Subscription
     where
-        A: Action,
-        V: 'static,
-        F: 'static + FnMut(&mut V, &A, &mut ViewContext<V>),
+        W: 'static,
+        E: Entity<W>,
     {
-        self.add_action_internal(handler, true)
+        self.observe_internal(entity, move |e, cx| {
+            on_notify(e, cx);
+            true
+        })
     }
 
-    fn add_action_internal<A, V, F, R>(&mut self, mut handler: F, capture: bool)
+    pub fn observe_internal<W, E>(
+        &mut self,
+        entity: &E,
+        mut on_notify: impl FnMut(E, &mut AppContext) -> bool + 'static,
+    ) -> Subscription
     where
-        A: Action,
-        V: 'static,
-        F: 'static + FnMut(&mut V, &A, &mut ViewContext<V>) -> R,
+        W: 'static,
+        E: Entity<W>,
     {
-        let handler = Box::new(
-            move |view: &mut dyn AnyView,
-                  action: &dyn Action,
-                  cx: &mut WindowContext,
-                  view_id: usize| {
-                let action = action.as_any().downcast_ref().unwrap();
-                let mut cx = ViewContext::mutable(cx, view_id);
-                handler(
-                    view.as_any_mut()
-                        .downcast_mut()
-                        .expect("downcast is type safe"),
-                    action,
-                    &mut cx,
-                );
-            },
-        );
-        fn inner(
-            this: &mut AppContext,
-            name: &'static str,
-            deserializer: fn(serde_json::Value) -> anyhow::Result<Box<dyn Action>>,
-            action_id: TypeId,
-            view_id: TypeId,
-            handler: Box<ActionCallback>,
-            capture: bool,
-        ) {
-            this.action_deserializers
-                .entry(name)
-                .or_insert((action_id.clone(), deserializer));
-
-            let actions = if capture {
-                &mut this.capture_actions
-            } else {
-                &mut this.actions
-            };
-
-            actions
-                .entry(view_id)
-                .or_default()
-                .entry(action_id)
-                .or_default()
-                .push(handler);
-        }
-        inner(
-            self,
-            A::qualified_name(),
-            A::from_json_str,
-            TypeId::of::<A>(),
-            TypeId::of::<V>(),
-            handler,
-            capture,
+        let entity_id = entity.entity_id();
+        let handle = entity.downgrade();
+        let (subscription, activate) = self.observers.insert(
+            entity_id,
+            Box::new(move |cx| {
+                if let Some(handle) = E::upgrade_from(&handle) {
+                    on_notify(handle, cx)
+                } else {
+                    false
+                }
+            }),
         );
+        self.defer(move |_| activate());
+        subscription
     }
 
-    pub fn add_async_action<A, V, F>(&mut self, mut handler: F)
+    pub fn subscribe<T, E, Evt>(
+        &mut self,
+        entity: &E,
+        mut on_event: impl FnMut(E, &Evt, &mut AppContext) + 'static,
+    ) -> Subscription
     where
-        A: Action,
-        V: 'static,
-        F: 'static + FnMut(&mut V, &A, &mut ViewContext<V>) -> Option<Task<Result<()>>>,
+        T: 'static + EventEmitter<Evt>,
+        E: Entity<T>,
+        Evt: 'static,
     {
-        self.add_action(move |view, action, cx| {
-            if let Some(task) = handler(view, action, cx) {
-                task.detach_and_log_err(cx);
-            }
+        self.subscribe_internal(entity, move |entity, event, cx| {
+            on_event(entity, event, cx);
+            true
         })
     }
 
-    pub fn add_global_action<A, F>(&mut self, mut handler: F)
+    pub(crate) fn subscribe_internal<T, E, Evt>(
+        &mut self,
+        entity: &E,
+        mut on_event: impl FnMut(E, &Evt, &mut AppContext) -> bool + 'static,
+    ) -> Subscription
     where
-        A: Action,
-        F: 'static + FnMut(&A, &mut AppContext),
+        T: 'static + EventEmitter<Evt>,
+        E: Entity<T>,
+        Evt: 'static,
     {
-        let handler = Box::new(move |action: &dyn Action, cx: &mut AppContext| {
-            let action = action.as_any().downcast_ref().unwrap();
-            handler(action, cx);
-        });
+        let entity_id = entity.entity_id();
+        let entity = entity.downgrade();
+        let (subscription, activate) = self.event_listeners.insert(
+            entity_id,
+            (
+                TypeId::of::<Evt>(),
+                Box::new(move |event, cx| {
+                    let event: &Evt = event.downcast_ref().expect("invalid event type");
+                    if let Some(handle) = E::upgrade_from(&entity) {
+                        on_event(handle, event, cx)
+                    } else {
+                        false
+                    }
+                }),
+            ),
+        );
+        self.defer(move |_| activate());
+        subscription
+    }
 
-        self.action_deserializers
-            .entry(A::qualified_name())
-            .or_insert((TypeId::of::<A>(), A::from_json_str));
+    pub fn windows(&self) -> Vec<AnyWindowHandle> {
+        self.windows
+            .values()
+            .filter_map(|window| Some(window.as_ref()?.handle))
+            .collect()
+    }
 
-        if self
-            .global_actions
-            .insert(TypeId::of::<A>(), handler)
-            .is_some()
-        {
-            panic!(
-                "registered multiple global handlers for {}",
-                type_name::<A>()
-            );
-        }
+    pub fn active_window(&self) -> Option<AnyWindowHandle> {
+        self.platform.active_window()
     }
 
-    pub fn view_ui_name(&self, window: AnyWindowHandle, view_id: usize) -> Option<&'static str> {
-        Some(self.views.get(&(window, view_id))?.ui_name())
+    /// Opens a new window with the given option and the root view returned by the given function.
+    /// The function is invoked with a `WindowContext`, which can be used to interact with window-specific
+    /// functionality.
+    pub fn open_window<V: 'static + Render>(
+        &mut self,
+        options: crate::WindowOptions,
+        build_root_view: impl FnOnce(&mut WindowContext) -> View<V>,
+    ) -> WindowHandle<V> {
+        self.update(|cx| {
+            let id = cx.windows.insert(None);
+            let handle = WindowHandle::new(id);
+            let mut window = Window::new(handle.into(), options, cx);
+            let root_view = build_root_view(&mut WindowContext::new(cx, &mut window));
+            window.root_view.replace(root_view.into());
+            cx.windows.get_mut(id).unwrap().replace(window);
+            handle
+        })
     }
 
-    pub fn view_type_id(&self, window: AnyWindowHandle, view_id: usize) -> Option<TypeId> {
-        self.views_metadata
-            .get(&(window, view_id))
-            .map(|metadata| metadata.type_id)
+    /// Instructs the platform to activate the application by bringing it to the foreground.
+    pub fn activate(&self, ignoring_other_apps: bool) {
+        self.platform.activate(ignoring_other_apps);
     }
 
-    pub fn active_labeled_tasks<'a>(
-        &'a self,
-    ) -> impl DoubleEndedIterator<Item = &'static str> + 'a {
-        self.active_labeled_tasks.values().cloned()
+    pub fn hide(&self) {
+        self.platform.hide();
     }
 
-    pub(crate) fn start_frame(&mut self) {
-        self.frame_count += 1;
+    pub fn hide_other_apps(&self) {
+        self.platform.hide_other_apps();
     }
 
-    pub fn update<T, F: FnOnce(&mut Self) -> T>(&mut self, callback: F) -> T {
-        self.pending_flushes += 1;
-        let result = callback(self);
-        self.flush_effects();
-        result
+    pub fn unhide_other_apps(&self) {
+        self.platform.unhide_other_apps();
     }
 
-    fn read_window<T, F: FnOnce(&WindowContext) -> T>(
-        &self,
-        handle: AnyWindowHandle,
-        callback: F,
-    ) -> Option<T> {
-        let window = self.windows.get(&handle)?;
-        let window_context = WindowContext::immutable(self, &window, handle);
-        Some(callback(&window_context))
+    /// Returns the list of currently active displays.
+    pub fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
+        self.platform.displays()
     }
 
-    pub fn update_active_window<T, F: FnOnce(&mut WindowContext) -> T>(
-        &mut self,
-        callback: F,
-    ) -> Option<T> {
-        self.active_window()
-            .and_then(|window| window.update(self, callback))
+    /// Writes data to the platform clipboard.
+    pub fn write_to_clipboard(&self, item: ClipboardItem) {
+        self.platform.write_to_clipboard(item)
+    }
+
+    /// Reads data from the platform clipboard.
+    pub fn read_from_clipboard(&self) -> Option<ClipboardItem> {
+        self.platform.read_from_clipboard()
+    }
+
+    /// Writes credentials to the platform keychain.
+    pub fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Result<()> {
+        self.platform.write_credentials(url, username, password)
+    }
+
+    /// Reads credentials from the platform keychain.
+    pub fn read_credentials(&self, url: &str) -> Result<Option<(String, Vec<u8>)>> {
+        self.platform.read_credentials(url)
+    }
+
+    /// Deletes credentials from the platform keychain.
+    pub fn delete_credentials(&self, url: &str) -> Result<()> {
+        self.platform.delete_credentials(url)
+    }
+
+    /// Directs the platform's default browser to open the given URL.
+    pub fn open_url(&self, url: &str) {
+        self.platform.open_url(url);
+    }
+
+    pub fn app_path(&self) -> Result<PathBuf> {
+        self.platform.app_path()
+    }
+
+    pub fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
+        self.platform.path_for_auxiliary_executable(name)
+    }
+
+    pub fn double_click_interval(&self) -> Duration {
+        self.platform.double_click_interval()
     }
 
     pub fn prompt_for_paths(
         &self,
         options: PathPromptOptions,
     ) -> oneshot::Receiver<Option<Vec<PathBuf>>> {
-        self.foreground_platform.prompt_for_paths(options)
+        self.platform.prompt_for_paths(options)
     }
 
     pub fn prompt_for_new_path(&self, directory: &Path) -> oneshot::Receiver<Option<PathBuf>> {
-        self.foreground_platform.prompt_for_new_path(directory)
+        self.platform.prompt_for_new_path(directory)
     }
 
     pub fn reveal_path(&self, path: &Path) {
-        self.foreground_platform.reveal_path(path)
-    }
-
-    pub fn emit_global<E: Any>(&mut self, payload: E) {
-        self.pending_effects.push_back(Effect::GlobalEvent {
-            payload: Box::new(payload),
-        });
+        self.platform.reveal_path(path)
     }
 
-    pub fn subscribe<E, H, F>(&mut self, handle: &H, mut callback: F) -> Subscription
-    where
-        E: Entity,
-        E::Event: 'static,
-        H: Handle<E>,
-        F: 'static + FnMut(H, &E::Event, &mut Self),
-    {
-        self.subscribe_internal(handle, move |handle, event, cx| {
-            callback(handle, event, cx);
-            true
-        })
+    pub fn should_auto_hide_scrollbars(&self) -> bool {
+        self.platform.should_auto_hide_scrollbars()
     }
 
-    pub fn subscribe_global<E, F>(&mut self, mut callback: F) -> Subscription
-    where
-        E: Any,
-        F: 'static + FnMut(&E, &mut Self),
-    {
-        let subscription_id = post_inc(&mut self.next_subscription_id);
-        let type_id = TypeId::of::<E>();
-        self.pending_effects.push_back(Effect::GlobalSubscription {
-            type_id,
-            subscription_id,
-            callback: Box::new(move |payload, cx| {
-                let payload = payload.downcast_ref().expect("downcast is type safe");
-                callback(payload, cx)
-            }),
-        });
-        Subscription::GlobalSubscription(
-            self.global_subscriptions
-                .subscribe(type_id, subscription_id),
-        )
+    pub fn restart(&self) {
+        self.platform.restart()
     }
 
-    pub fn observe<E, H, F>(&mut self, handle: &H, mut callback: F) -> Subscription
-    where
-        E: Entity,
-        E::Event: 'static,
-        H: Handle<E>,
-        F: 'static + FnMut(H, &mut Self),
-    {
-        self.observe_internal(handle, move |handle, cx| {
-            callback(handle, cx);
-            true
-        })
+    pub fn local_timezone(&self) -> UtcOffset {
+        self.platform.local_timezone()
     }
 
-    fn subscribe_internal<E, H, F>(&mut self, handle: &H, mut callback: F) -> Subscription
-    where
-        E: Entity,
-        E::Event: 'static,
-        H: Handle<E>,
-        F: 'static + FnMut(H, &E::Event, &mut Self) -> bool,
-    {
-        let subscription_id = post_inc(&mut self.next_subscription_id);
-        let emitter = handle.downgrade();
-        self.pending_effects.push_back(Effect::Subscription {
-            entity_id: handle.id(),
-            subscription_id,
-            callback: Box::new(move |payload, cx| {
-                if let Some(emitter) = H::upgrade_from(&emitter, cx) {
-                    let payload = payload.downcast_ref().expect("downcast is type safe");
-                    callback(emitter, payload, cx)
-                } else {
-                    false
+    pub(crate) fn push_effect(&mut self, effect: Effect) {
+        match &effect {
+            Effect::Notify { emitter } => {
+                if !self.pending_notifications.insert(*emitter) {
+                    return;
                 }
-            }),
-        });
-        Subscription::Subscription(self.subscriptions.subscribe(handle.id(), subscription_id))
-    }
-
-    fn observe_internal<E, H, F>(&mut self, handle: &H, mut callback: F) -> Subscription
-    where
-        E: Entity,
-        E::Event: 'static,
-        H: Handle<E>,
-        F: 'static + FnMut(H, &mut Self) -> bool,
-    {
-        let subscription_id = post_inc(&mut self.next_subscription_id);
-        let observed = handle.downgrade();
-        let entity_id = handle.id();
-        self.pending_effects.push_back(Effect::Observation {
-            entity_id,
-            subscription_id,
-            callback: Box::new(move |cx| {
-                if let Some(observed) = H::upgrade_from(&observed, cx) {
-                    callback(observed, cx)
-                } else {
-                    false
+            }
+            Effect::NotifyGlobalObservers { global_type } => {
+                if !self.pending_global_notifications.insert(*global_type) {
+                    return;
                 }
-            }),
-        });
-        Subscription::Observation(self.observations.subscribe(entity_id, subscription_id))
-    }
+            }
+            _ => {}
+        };
 
-    fn observe_focus<F, V>(&mut self, handle: &ViewHandle<V>, mut callback: F) -> Subscription
-    where
-        V: 'static,
-        F: 'static + FnMut(ViewHandle<V>, bool, &mut WindowContext) -> bool,
-    {
-        let subscription_id = post_inc(&mut self.next_subscription_id);
-        let observed = handle.downgrade();
-        let view_id = handle.id();
-
-        self.pending_effects.push_back(Effect::FocusObservation {
-            view_id,
-            subscription_id,
-            callback: Box::new(move |focused, cx| {
-                if let Some(observed) = observed.upgrade(cx) {
-                    callback(observed, focused, cx)
-                } else {
-                    false
-                }
-            }),
-        });
-        Subscription::FocusObservation(self.focus_observations.subscribe(view_id, subscription_id))
+        self.pending_effects.push_back(effect);
     }
 
-    pub fn observe_global<G, F>(&mut self, mut observe: F) -> Subscription
-    where
-        G: Any,
-        F: 'static + FnMut(&mut AppContext),
-    {
-        let type_id = TypeId::of::<G>();
-        let id = post_inc(&mut self.next_subscription_id);
+    /// Called at the end of AppContext::update to complete any side effects
+    /// such as notifying observers, emitting events, etc. Effects can themselves
+    /// cause effects, so we continue looping until all effects are processed.
+    fn flush_effects(&mut self) {
+        loop {
+            self.release_dropped_entities();
+            self.release_dropped_focus_handles();
 
-        self.global_observations.add_callback(
-            type_id,
-            id,
-            Box::new(move |cx: &mut AppContext| observe(cx)),
-        );
-        Subscription::GlobalObservation(self.global_observations.subscribe(type_id, id))
-    }
+            if let Some(effect) = self.pending_effects.pop_front() {
+                match effect {
+                    Effect::Notify { emitter } => {
+                        self.apply_notify_effect(emitter);
+                    }
 
-    pub fn observe_default_global<G, F>(&mut self, observe: F) -> Subscription
-    where
-        G: Any + Default,
-        F: 'static + FnMut(&mut AppContext),
-    {
-        if !self.has_global::<G>() {
-            self.set_global(G::default());
+                    Effect::Emit {
+                        emitter,
+                        event_type,
+                        event,
+                    } => self.apply_emit_effect(emitter, event_type, event),
+
+                    Effect::Refresh => {
+                        self.apply_refresh_effect();
+                    }
+
+                    Effect::NotifyGlobalObservers { global_type } => {
+                        self.apply_notify_global_observers_effect(global_type);
+                    }
+
+                    Effect::Defer { callback } => {
+                        self.apply_defer_effect(callback);
+                    }
+                }
+            } else {
+                for window in self.windows.values() {
+                    if let Some(window) = window.as_ref() {
+                        if window.dirty {
+                            window.platform_window.invalidate();
+                        }
+                    }
+                }
+
+                #[cfg(any(test, feature = "test-support"))]
+                for window in self
+                    .windows
+                    .values()
+                    .filter_map(|window| {
+                        let window = window.as_ref()?;
+                        (window.dirty || window.focus_invalidated).then_some(window.handle)
+                    })
+                    .collect::<Vec<_>>()
+                {
+                    self.update_window(window, |_, cx| cx.draw()).unwrap();
+                }
+
+                if self.pending_effects.is_empty() {
+                    break;
+                }
+            }
         }
-        self.observe_global::<G, F>(observe)
     }
 
-    pub fn observe_release<E, H, F>(&mut self, handle: &H, callback: F) -> Subscription
-    where
-        E: Entity,
-        E::Event: 'static,
-        H: Handle<E>,
-        F: 'static + FnOnce(&E, &mut Self),
-    {
-        let id = post_inc(&mut self.next_subscription_id);
-        let mut callback = Some(callback);
-        self.release_observations.add_callback(
-            handle.id(),
-            id,
-            Box::new(move |released, cx| {
-                let released = released.downcast_ref().unwrap();
-                if let Some(callback) = callback.take() {
-                    callback(released, cx)
+    /// Repeatedly called during `flush_effects` to release any entities whose
+    /// reference count has become zero. We invoke any release observers before dropping
+    /// each entity.
+    fn release_dropped_entities(&mut self) {
+        loop {
+            let dropped = self.entities.take_dropped();
+            if dropped.is_empty() {
+                break;
+            }
+
+            for (entity_id, mut entity) in dropped {
+                self.observers.remove(&entity_id);
+                self.event_listeners.remove(&entity_id);
+                for release_callback in self.release_listeners.remove(&entity_id) {
+                    release_callback(entity.as_mut(), self);
                 }
-            }),
-        );
-        Subscription::ReleaseObservation(self.release_observations.subscribe(handle.id(), id))
+            }
+        }
     }
 
-    pub fn observe_actions<F>(&mut self, callback: F) -> Subscription
-    where
-        F: 'static + FnMut(TypeId, &mut AppContext),
-    {
-        let subscription_id = post_inc(&mut self.next_subscription_id);
-        self.action_dispatch_observations
-            .add_callback((), subscription_id, Box::new(callback));
-        Subscription::ActionObservation(
-            self.action_dispatch_observations
-                .subscribe((), subscription_id),
-        )
-    }
+    /// Repeatedly called during `flush_effects` to handle a focused handle being dropped.
+    fn release_dropped_focus_handles(&mut self) {
+        for window_handle in self.windows() {
+            window_handle
+                .update(self, |_, cx| {
+                    let mut blur_window = false;
+                    let focus = cx.window.focus;
+                    cx.window.focus_handles.write().retain(|handle_id, count| {
+                        if count.load(SeqCst) == 0 {
+                            if focus == Some(handle_id) {
+                                blur_window = true;
+                            }
+                            false
+                        } else {
+                            true
+                        }
+                    });
 
-    fn observe_active_labeled_tasks<F>(&mut self, callback: F) -> Subscription
-    where
-        F: 'static + FnMut(&mut AppContext) -> bool,
-    {
-        let subscription_id = post_inc(&mut self.next_subscription_id);
-        self.active_labeled_task_observations
-            .add_callback((), subscription_id, Box::new(callback));
-        Subscription::ActiveLabeledTasksObservation(
-            self.active_labeled_task_observations
-                .subscribe((), subscription_id),
-        )
+                    if blur_window {
+                        cx.blur();
+                    }
+                })
+                .unwrap();
+        }
     }
 
-    pub fn defer(&mut self, callback: impl 'static + FnOnce(&mut AppContext)) {
-        self.pending_effects.push_back(Effect::Deferred {
-            callback: Box::new(callback),
-            after_window_update: false,
-        })
+    fn apply_notify_effect(&mut self, emitter: EntityId) {
+        self.pending_notifications.remove(&emitter);
+
+        self.observers
+            .clone()
+            .retain(&emitter, |handler| handler(self));
     }
 
-    pub fn after_window_update(&mut self, callback: impl 'static + FnOnce(&mut AppContext)) {
-        self.pending_effects.push_back(Effect::Deferred {
-            callback: Box::new(callback),
-            after_window_update: true,
-        })
+    fn apply_emit_effect(&mut self, emitter: EntityId, event_type: TypeId, event: Box<dyn Any>) {
+        self.event_listeners
+            .clone()
+            .retain(&emitter, |(stored_type, handler)| {
+                if *stored_type == event_type {
+                    handler(event.as_ref(), self)
+                } else {
+                    true
+                }
+            });
     }
 
-    fn notify_model(&mut self, model_id: usize) {
-        if self.pending_notifications.insert(model_id) {
-            self.pending_effects
-                .push_back(Effect::ModelNotification { model_id });
+    fn apply_refresh_effect(&mut self) {
+        for window in self.windows.values_mut() {
+            if let Some(window) = window.as_mut() {
+                window.dirty = true;
+            }
         }
     }
 
-    fn notify_view(&mut self, window: AnyWindowHandle, view_id: usize) {
-        if self.pending_notifications.insert(view_id) {
-            self.pending_effects
-                .push_back(Effect::ViewNotification { window, view_id });
-        }
+    fn apply_notify_global_observers_effect(&mut self, type_id: TypeId) {
+        self.pending_global_notifications.remove(&type_id);
+        self.global_observers
+            .clone()
+            .retain(&type_id, |observer| observer(self));
+    }
+
+    fn apply_defer_effect(&mut self, callback: Box<dyn FnOnce(&mut Self) + 'static>) {
+        callback(self);
     }
 
-    fn notify_global(&mut self, type_id: TypeId) {
-        if self.pending_global_notifications.insert(type_id) {
-            self.pending_effects
-                .push_back(Effect::GlobalNotification { type_id });
+    /// Creates an `AsyncAppContext`, which can be cloned and has a static lifetime
+    /// so it can be held across `await` points.
+    pub fn to_async(&self) -> AsyncAppContext {
+        AsyncAppContext {
+            app: unsafe { mem::transmute(self.this.clone()) },
+            background_executor: self.background_executor.clone(),
+            foreground_executor: self.foreground_executor.clone(),
         }
     }
 
-    pub fn all_action_names<'a>(&'a self) -> impl Iterator<Item = &'static str> + 'a {
-        self.action_deserializers.keys().copied()
+    /// Obtains a reference to the executor, which can be used to spawn futures.
+    pub fn background_executor(&self) -> &BackgroundExecutor {
+        &self.background_executor
     }
 
-    pub fn is_action_available(&self, action: &dyn Action) -> bool {
-        let mut available_in_window = false;
-        let action_id = action.id();
-        if let Some(window) = self.active_window() {
-            available_in_window = self
-                .read_window(window, |cx| {
-                    if let Some(focused_view_id) = cx.focused_view_id() {
-                        for view_id in cx.ancestors(focused_view_id) {
-                            if let Some(view_metadata) =
-                                cx.views_metadata.get(&(cx.window_handle, view_id))
-                            {
-                                if let Some(actions) = cx.actions.get(&view_metadata.type_id) {
-                                    if actions.contains_key(&action_id) {
-                                        return true;
-                                    }
-                                }
-                            }
-                        }
-                    }
-                    false
-                })
-                .unwrap_or(false);
-        }
-        available_in_window || self.global_actions.contains_key(&action_id)
+    /// Obtains a reference to the executor, which can be used to spawn futures.
+    pub fn foreground_executor(&self) -> &ForegroundExecutor {
+        &self.foreground_executor
     }
 
-    fn actions_mut(
-        &mut self,
-        capture_phase: bool,
-    ) -> &mut HashMap<TypeId, HashMap<TypeId, Vec<Box<ActionCallback>>>> {
-        if capture_phase {
-            &mut self.capture_actions
-        } else {
-            &mut self.actions
-        }
+    /// Spawns the future returned by the given function on the thread pool. The closure will be invoked
+    /// with AsyncAppContext, which allows the application state to be accessed across await points.
+    pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task<R>
+    where
+        Fut: Future<Output = R> + 'static,
+        R: 'static,
+    {
+        self.foreground_executor.spawn(f(self.to_async()))
     }
 
-    fn dispatch_global_action_any(&mut self, action: &dyn Action) -> bool {
-        self.update(|this| {
-            if let Some((name, mut handler)) = this.global_actions.remove_entry(&action.id()) {
-                handler(action, this);
-                this.global_actions.insert(name, handler);
-                true
-            } else {
-                false
-            }
-        })
+    /// Schedules the given function to be run at the end of the current effect cycle, allowing entities
+    /// that are currently on the stack to be returned to the app.
+    pub fn defer(&mut self, f: impl FnOnce(&mut AppContext) + 'static) {
+        self.push_effect(Effect::Defer {
+            callback: Box::new(f),
+        });
     }
 
-    pub fn add_bindings<T: IntoIterator<Item = Binding>>(&mut self, bindings: T) {
-        self.keystroke_matcher.add_bindings(bindings);
+    /// Accessor for the application's asset source, which is provided when constructing the `App`.
+    pub fn asset_source(&self) -> &Arc<dyn AssetSource> {
+        &self.asset_source
     }
 
-    pub fn clear_bindings(&mut self) {
-        self.keystroke_matcher.clear_bindings();
+    /// Accessor for the text system.
+    pub fn text_system(&self) -> &Arc<TextSystem> {
+        &self.text_system
     }
 
-    pub fn binding_for_action(&self, action: &dyn Action) -> Option<&Binding> {
-        self.keystroke_matcher
-            .bindings_for_action(action.id())
-            .find(|binding| binding.action().eq(action))
+    /// The current text style. Which is composed of all the style refinements provided to `with_text_style`.
+    pub fn text_style(&self) -> TextStyle {
+        let mut style = TextStyle::default();
+        for refinement in &self.text_style_stack {
+            style.refine(refinement);
+        }
+        style
     }
 
-    pub fn default_global<T: 'static + Default>(&mut self) -> &T {
-        let type_id = TypeId::of::<T>();
-        self.update(|this| {
-            if let Entry::Vacant(entry) = this.globals.entry(type_id) {
-                entry.insert(Box::new(T::default()));
-                this.notify_global(type_id);
-            }
-        });
-        self.globals.get(&type_id).unwrap().downcast_ref().unwrap()
+    /// Check whether a global of the given type has been assigned.
+    pub fn has_global<G: 'static>(&self) -> bool {
+        self.globals_by_type.contains_key(&TypeId::of::<G>())
     }
 
-    pub fn set_global<T: 'static>(&mut self, state: T) {
-        self.update(|this| {
-            let type_id = TypeId::of::<T>();
-            this.globals.insert(type_id, Box::new(state));
-            this.notify_global(type_id);
-        });
+    /// Access the global of the given type. Panics if a global for that type has not been assigned.
+    #[track_caller]
+    pub fn global<G: 'static>(&self) -> &G {
+        self.globals_by_type
+            .get(&TypeId::of::<G>())
+            .map(|any_state| any_state.downcast_ref::<G>().unwrap())
+            .ok_or_else(|| anyhow!("no state of type {} exists", type_name::<G>()))
+            .unwrap()
     }
 
-    pub fn update_default_global<T, F, U>(&mut self, update: F) -> U
-    where
-        T: 'static + Default,
-        F: FnOnce(&mut T, &mut AppContext) -> U,
-    {
-        self.update(|mut this| {
-            Self::update_default_global_internal(&mut this, |global, cx| update(global, cx))
-        })
+    /// Access the global of the given type if a value has been assigned.
+    pub fn try_global<G: 'static>(&self) -> Option<&G> {
+        self.globals_by_type
+            .get(&TypeId::of::<G>())
+            .map(|any_state| any_state.downcast_ref::<G>().unwrap())
     }
 
-    fn update_default_global_internal<C, T, F, U>(this: &mut C, update: F) -> U
-    where
-        C: DerefMut<Target = AppContext>,
-        T: 'static + Default,
-        F: FnOnce(&mut T, &mut C) -> U,
-    {
-        let type_id = TypeId::of::<T>();
-        let mut state = this
-            .globals
-            .remove(&type_id)
-            .unwrap_or_else(|| Box::new(T::default()));
-        let result = update(state.downcast_mut().unwrap(), this);
-        this.globals.insert(type_id, state);
-        this.notify_global(type_id);
-        result
+    /// Access the global of the given type mutably. Panics if a global for that type has not been assigned.
+    #[track_caller]
+    pub fn global_mut<G: 'static>(&mut self) -> &mut G {
+        let global_type = TypeId::of::<G>();
+        self.push_effect(Effect::NotifyGlobalObservers { global_type });
+        self.globals_by_type
+            .get_mut(&global_type)
+            .and_then(|any_state| any_state.downcast_mut::<G>())
+            .ok_or_else(|| anyhow!("no state of type {} exists", type_name::<G>()))
+            .unwrap()
     }
 
-    pub fn update_global<T, F, U>(&mut self, update: F) -> U
-    where
-        T: 'static,
-        F: FnOnce(&mut T, &mut AppContext) -> U,
-    {
-        self.update(|mut this| {
-            Self::update_global_internal(&mut this, |global, cx| update(global, cx))
-        })
+    /// Access the global of the given type mutably. A default value is assigned if a global of this type has not
+    /// yet been assigned.
+    pub fn default_global<G: 'static + Default>(&mut self) -> &mut G {
+        let global_type = TypeId::of::<G>();
+        self.push_effect(Effect::NotifyGlobalObservers { global_type });
+        self.globals_by_type
+            .entry(global_type)
+            .or_insert_with(|| Box::<G>::default())
+            .downcast_mut::<G>()
+            .unwrap()
     }
 
-    fn update_global_internal<C, T, F, U>(this: &mut C, update: F) -> U
-    where
-        C: DerefMut<Target = AppContext>,
-        T: 'static,
-        F: FnOnce(&mut T, &mut C) -> U,
-    {
-        let type_id = TypeId::of::<T>();
-        if let Some(mut state) = this.globals.remove(&type_id) {
-            let result = update(state.downcast_mut().unwrap(), this);
-            this.globals.insert(type_id, state);
-            this.notify_global(type_id);
-            result
-        } else {
-            panic!("no global added for {}", std::any::type_name::<T>());
-        }
+    /// Set the value of the global of the given type.
+    pub fn set_global<G: Any>(&mut self, global: G) {
+        let global_type = TypeId::of::<G>();
+        self.push_effect(Effect::NotifyGlobalObservers { global_type });
+        self.globals_by_type.insert(global_type, Box::new(global));
     }
 
+    /// Clear all stored globals. Does not notify global observers.
+    #[cfg(any(test, feature = "test-support"))]
     pub fn clear_globals(&mut self) {
-        self.globals.clear();
+        self.globals_by_type.drain();
     }
 
-    pub fn remove_global<T: 'static>(&mut self) -> T {
+    /// Remove the global of the given type from the app context. Does not notify global observers.
+    pub fn remove_global<G: Any>(&mut self) -> G {
+        let global_type = TypeId::of::<G>();
         *self
-            .globals
-            .remove(&TypeId::of::<T>())
-            .unwrap_or_else(|| panic!("no global added for {}", std::any::type_name::<T>()))
+            .globals_by_type
+            .remove(&global_type)
+            .unwrap_or_else(|| panic!("no global added for {}", std::any::type_name::<G>()))
             .downcast()
             .unwrap()
     }
 
-    pub fn add_model<T, F>(&mut self, build_model: F) -> ModelHandle<T>
-    where
-        T: Entity,
-        F: FnOnce(&mut ModelContext<T>) -> T,
-    {
-        self.update(|this| {
-            let model_id = post_inc(&mut this.next_id);
-            let handle = ModelHandle::new(model_id, &this.ref_counts);
-            let mut cx = ModelContext::new(this, model_id);
-            let model = build_model(&mut cx);
-            this.models.insert(model_id, Box::new(model));
-            handle
-        })
-    }
-
-    pub fn read_model<T: Entity>(&self, handle: &ModelHandle<T>) -> &T {
-        if let Some(model) = self.models.get(&handle.model_id) {
-            model
-                .as_any()
-                .downcast_ref()
-                .expect("downcast is type safe")
-        } else {
-            panic!("circular model reference");
-        }
+    /// Update the global of the given type with a closure. Unlike `global_mut`, this method provides
+    /// your closure with mutable access to the `AppContext` and the global simultaneously.
+    pub fn update_global<G: 'static, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R {
+        let mut global = self.lease_global::<G>();
+        let result = f(&mut global, self);
+        self.end_global_lease(global);
+        result
     }
 
-    fn update_model<T: Entity, V>(
+    /// Register a callback to be invoked when a global of the given type is updated.
+    pub fn observe_global<G: 'static>(
         &mut self,
-        handle: &ModelHandle<T>,
-        update: &mut dyn FnMut(&mut T, &mut ModelContext<T>) -> V,
-    ) -> V {
-        if let Some(mut model) = self.models.remove(&handle.model_id) {
-            self.update(|this| {
-                let mut cx = ModelContext::new(this, handle.model_id);
-                let result = update(
-                    model
-                        .as_any_mut()
-                        .downcast_mut()
-                        .expect("downcast is type safe"),
-                    &mut cx,
-                );
-                this.models.insert(handle.model_id, model);
-                result
-            })
-        } else {
-            panic!("circular model update for {}", std::any::type_name::<T>());
-        }
-    }
-
-    fn upgrade_model_handle<T: Entity>(
-        &self,
-        handle: &WeakModelHandle<T>,
-    ) -> Option<ModelHandle<T>> {
-        if self.ref_counts.lock().is_entity_alive(handle.model_id) {
-            Some(ModelHandle::new(handle.model_id, &self.ref_counts))
-        } else {
-            None
-        }
+        mut f: impl FnMut(&mut Self) + 'static,
+    ) -> Subscription {
+        let (subscription, activate) = self.global_observers.insert(
+            TypeId::of::<G>(),
+            Box::new(move |cx| {
+                f(cx);
+                true
+            }),
+        );
+        self.defer(move |_| activate());
+        subscription
     }
 
-    fn model_handle_is_upgradable<T: Entity>(&self, handle: &WeakModelHandle<T>) -> bool {
-        self.ref_counts.lock().is_entity_alive(handle.model_id)
+    /// Move the global of the given type to the stack.
+    pub(crate) fn lease_global<G: 'static>(&mut self) -> GlobalLease<G> {
+        GlobalLease::new(
+            self.globals_by_type
+                .remove(&TypeId::of::<G>())
+                .ok_or_else(|| anyhow!("no global registered of type {}", type_name::<G>()))
+                .unwrap(),
+        )
     }
 
-    fn upgrade_any_model_handle(&self, handle: &AnyWeakModelHandle) -> Option<AnyModelHandle> {
-        if self.ref_counts.lock().is_entity_alive(handle.model_id) {
-            Some(AnyModelHandle::new(
-                handle.model_id,
-                handle.model_type,
-                self.ref_counts.clone(),
-            ))
-        } else {
-            None
-        }
+    /// Restore the global of the given type after it is moved to the stack.
+    pub(crate) fn end_global_lease<G: 'static>(&mut self, lease: GlobalLease<G>) {
+        let global_type = TypeId::of::<G>();
+        self.push_effect(Effect::NotifyGlobalObservers { global_type });
+        self.globals_by_type.insert(global_type, lease.global);
     }
 
-    pub fn add_window<V, F>(
+    pub fn observe_new_views<V: 'static>(
         &mut self,
-        window_options: WindowOptions,
-        build_root_view: F,
-    ) -> WindowHandle<V>
-    where
-        V: View,
-        F: FnOnce(&mut ViewContext<V>) -> V,
-    {
-        self.update(|this| {
-            let handle = WindowHandle::<V>::new(post_inc(&mut this.next_id));
-            let platform_window =
-                this.platform
-                    .open_window(handle.into(), window_options, this.foreground.clone());
-            let window = this.build_window(handle.into(), platform_window, build_root_view);
-            this.windows.insert(handle.into(), window);
-            handle
-        })
+        on_new: impl 'static + Fn(&mut V, &mut ViewContext<V>),
+    ) -> Subscription {
+        let (subscription, activate) = self.new_view_observers.insert(
+            TypeId::of::<V>(),
+            Box::new(move |any_view: AnyView, cx: &mut WindowContext| {
+                any_view
+                    .downcast::<V>()
+                    .unwrap()
+                    .update(cx, |view_state, cx| {
+                        on_new(view_state, cx);
+                    })
+            }),
+        );
+        activate();
+        subscription
     }
 
-    pub fn add_status_bar_item<V, F>(&mut self, build_root_view: F) -> WindowHandle<V>
+    pub fn observe_release<E, T>(
+        &mut self,
+        handle: &E,
+        on_release: impl FnOnce(&mut T, &mut AppContext) + 'static,
+    ) -> Subscription
     where
-        V: View,
-        F: FnOnce(&mut ViewContext<V>) -> V,
+        E: Entity<T>,
+        T: 'static,
     {
-        self.update(|this| {
-            let handle = WindowHandle::<V>::new(post_inc(&mut this.next_id));
-            let platform_window = this.platform.add_status_item(handle.into());
-            let window = this.build_window(handle.into(), platform_window, build_root_view);
-            this.windows.insert(handle.into(), window);
-            handle.update_root(this, |view, cx| view.focus_in(cx.handle().into_any(), cx));
-            handle
-        })
+        let (subscription, activate) = self.release_listeners.insert(
+            handle.entity_id(),
+            Box::new(move |entity, cx| {
+                let entity = entity.downcast_mut().expect("invalid entity type");
+                on_release(entity, cx)
+            }),
+        );
+        activate();
+        subscription
     }
 
-    pub fn build_window<V, F>(
+    pub fn observe_keystrokes(
         &mut self,
-        handle: AnyWindowHandle,
-        mut platform_window: Box<dyn platform::Window>,
-        build_root_view: F,
-    ) -> Window
-    where
-        V: View,
-        F: FnOnce(&mut ViewContext<V>) -> V,
-    {
-        {
-            let mut app = self.upgrade();
-
-            platform_window.on_event(Box::new(move |event| {
-                app.update_window(handle, |cx| {
-                    if let Event::KeyDown(KeyDownEvent { keystroke, .. }) = &event {
-                        if cx.dispatch_keystroke(keystroke) {
-                            return true;
-                        }
-                    }
-
-                    cx.dispatch_event(event, false)
-                })
-                .unwrap_or(false)
-            }));
-        }
-
-        {
-            let mut app = self.upgrade();
-            platform_window.on_active_status_change(Box::new(move |is_active| {
-                app.update(|cx| cx.window_changed_active_status(handle, is_active))
-            }));
-        }
+        f: impl FnMut(&KeystrokeEvent, &mut WindowContext) + 'static,
+    ) -> Subscription {
+        let (subscription, activate) = self.keystroke_observers.insert((), Box::new(f));
+        activate();
+        subscription
+    }
 
-        {
-            let mut app = self.upgrade();
-            platform_window.on_resize(Box::new(move || {
-                app.update(|cx| cx.window_was_resized(handle))
-            }));
-        }
+    pub(crate) fn push_text_style(&mut self, text_style: TextStyleRefinement) {
+        self.text_style_stack.push(text_style);
+    }
 
-        {
-            let mut app = self.upgrade();
-            platform_window.on_moved(Box::new(move || {
-                app.update(|cx| cx.window_was_moved(handle))
-            }));
-        }
+    pub(crate) fn pop_text_style(&mut self) {
+        self.text_style_stack.pop();
+    }
 
-        {
-            let mut app = self.upgrade();
-            platform_window.on_fullscreen(Box::new(move |is_fullscreen| {
-                app.update(|cx| cx.window_was_fullscreen_changed(handle, is_fullscreen))
-            }));
-        }
+    /// Register key bindings.
+    pub fn bind_keys(&mut self, bindings: impl IntoIterator<Item = KeyBinding>) {
+        self.keymap.lock().add_bindings(bindings);
+        self.pending_effects.push_back(Effect::Refresh);
+    }
 
-        {
-            let mut app = self.upgrade();
-            platform_window.on_close(Box::new(move || {
-                app.update(|cx| cx.update_window(handle, |cx| cx.remove_window()));
+    /// Register a global listener for actions invoked via the keyboard.
+    pub fn on_action<A: Action>(&mut self, listener: impl Fn(&A, &mut Self) + 'static) {
+        self.global_action_listeners
+            .entry(TypeId::of::<A>())
+            .or_default()
+            .push(Rc::new(move |action, phase, cx| {
+                if phase == DispatchPhase::Bubble {
+                    let action = action.downcast_ref().unwrap();
+                    listener(action, cx)
+                }
             }));
-        }
+    }
 
-        {
-            let mut app = self.upgrade();
-            platform_window
-                .on_appearance_changed(Box::new(move || app.update(|cx| cx.refresh_windows())));
-        }
+    /// Event handlers propagate events by default. Call this method to stop dispatching to
+    /// event handlers with a lower z-index (mouse) or higher in the tree (keyboard). This is
+    /// the opposite of [propagate]. It's also possible to cancel a call to [propagate] by
+    /// calling this method before effects are flushed.
+    pub fn stop_propagation(&mut self) {
+        self.propagate_event = false;
+    }
 
-        platform_window.set_input_handler(Box::new(WindowInputHandler {
-            app: self.upgrade().0,
-            window: handle,
-        }));
+    /// Action handlers stop propagation by default during the bubble phase of action dispatch
+    /// dispatching to action handlers higher in the element tree. This is the opposite of
+    /// [stop_propagation]. It's also possible to cancel a call to [stop_propagate] by calling
+    /// this method before effects are flushed.
+    pub fn propagate(&mut self) {
+        self.propagate_event = true;
+    }
 
-        let mut window = Window::new(handle, platform_window, self, build_root_view);
-        let mut cx = WindowContext::mutable(self, &mut window, handle);
-        cx.layout(false).expect("initial layout should not error");
-        let scene = cx.paint().expect("initial paint should not error");
-        window.platform_window.present_scene(scene);
-        window
+    pub fn build_action(
+        &self,
+        name: &str,
+        data: Option<serde_json::Value>,
+    ) -> Result<Box<dyn Action>> {
+        self.actions.build_action(name, data)
     }
 
-    pub fn active_window(&self) -> Option<AnyWindowHandle> {
-        self.platform.main_window()
+    pub fn all_action_names(&self) -> &[SharedString] {
+        self.actions.all_action_names()
     }
 
-    pub fn windows(&self) -> impl '_ + Iterator<Item = AnyWindowHandle> {
-        self.windows.keys().copied()
+    pub fn on_app_quit<Fut>(
+        &mut self,
+        mut on_quit: impl FnMut(&mut AppContext) -> Fut + 'static,
+    ) -> Subscription
+    where
+        Fut: 'static + Future<Output = ()>,
+    {
+        let (subscription, activate) = self.quit_observers.insert(
+            (),
+            Box::new(move |cx| {
+                let future = on_quit(cx);
+                future.boxed_local()
+            }),
+        );
+        activate();
+        subscription
     }
 
-    pub fn read_view<V: 'static>(&self, handle: &ViewHandle<V>) -> &V {
-        if let Some(view) = self.views.get(&(handle.window, handle.view_id)) {
-            view.as_any().downcast_ref().expect("downcast is type safe")
-        } else {
-            panic!("circular view reference for type {}", type_name::<V>());
+    pub(crate) fn clear_pending_keystrokes(&mut self) {
+        for window in self.windows() {
+            window
+                .update(self, |_, cx| {
+                    cx.window
+                        .rendered_frame
+                        .dispatch_tree
+                        .clear_pending_keystrokes();
+                    cx.window
+                        .next_frame
+                        .dispatch_tree
+                        .clear_pending_keystrokes();
+                })
+                .ok();
         }
     }
 
-    fn upgrade_view_handle<V: 'static>(&self, handle: &WeakViewHandle<V>) -> Option<ViewHandle<V>> {
-        if self.ref_counts.lock().is_entity_alive(handle.view_id) {
-            Some(ViewHandle::new(
-                handle.window,
-                handle.view_id,
-                &self.ref_counts,
-            ))
-        } else {
-            None
+    pub fn is_action_available(&mut self, action: &dyn Action) -> bool {
+        if let Some(window) = self.active_window() {
+            if let Ok(window_action_available) =
+                window.update(self, |_, cx| cx.is_action_available(action))
+            {
+                return window_action_available;
+            }
         }
+
+        self.global_action_listeners
+            .contains_key(&action.as_any().type_id())
     }
 
-    fn upgrade_any_view_handle(&self, handle: &AnyWeakViewHandle) -> Option<AnyViewHandle> {
-        if self.ref_counts.lock().is_entity_alive(handle.view_id) {
-            Some(AnyViewHandle::new(
-                handle.window,
-                handle.view_id,
-                handle.view_type,
-                self.ref_counts.clone(),
-            ))
-        } else {
-            None
-        }
+    pub fn set_menus(&mut self, menus: Vec<Menu>) {
+        self.platform.set_menus(menus, &self.keymap.lock());
     }
 
-    fn remove_dropped_entities(&mut self) {
-        loop {
-            let (dropped_models, dropped_views, dropped_element_states) =
-                self.ref_counts.lock().take_dropped();
-            if dropped_models.is_empty()
-                && dropped_views.is_empty()
-                && dropped_element_states.is_empty()
+    pub fn dispatch_action(&mut self, action: &dyn Action) {
+        if let Some(active_window) = self.active_window() {
+            active_window
+                .update(self, |_, cx| cx.dispatch_action(action.boxed_clone()))
+                .log_err();
+        } else {
+            self.propagate_event = true;
+
+            if let Some(mut global_listeners) = self
+                .global_action_listeners
+                .remove(&action.as_any().type_id())
             {
-                break;
-            }
+                for listener in &global_listeners {
+                    listener(action.as_any(), DispatchPhase::Capture, self);
+                    if !self.propagate_event {
+                        break;
+                    }
+                }
+
+                global_listeners.extend(
+                    self.global_action_listeners
+                        .remove(&action.as_any().type_id())
+                        .unwrap_or_default(),
+                );
 
-            for model_id in dropped_models {
-                self.subscriptions.remove(model_id);
-                self.observations.remove(model_id);
-                let mut model = self.models.remove(&model_id).unwrap();
-                model.release(self);
-                self.pending_effects
-                    .push_back(Effect::ModelRelease { model_id, model });
+                self.global_action_listeners
+                    .insert(action.as_any().type_id(), global_listeners);
             }
 
-            for (window, view_id) in dropped_views {
-                self.subscriptions.remove(view_id);
-                self.observations.remove(view_id);
-                self.views_metadata.remove(&(window, view_id));
-                let mut view = self.views.remove(&(window, view_id)).unwrap();
-                view.release(self);
-                if let Some(window) = self.windows.get_mut(&window) {
-                    window.parents.remove(&view_id);
-                    window
-                        .invalidation
-                        .get_or_insert_with(Default::default)
-                        .removed
-                        .push(view_id);
-                }
+            if self.propagate_event {
+                if let Some(mut global_listeners) = self
+                    .global_action_listeners
+                    .remove(&action.as_any().type_id())
+                {
+                    for listener in global_listeners.iter().rev() {
+                        listener(action.as_any(), DispatchPhase::Bubble, self);
+                        if !self.propagate_event {
+                            break;
+                        }
+                    }
 
-                self.pending_effects
-                    .push_back(Effect::ViewRelease { view_id, view });
-            }
+                    global_listeners.extend(
+                        self.global_action_listeners
+                            .remove(&action.as_any().type_id())
+                            .unwrap_or_default(),
+                    );
 
-            for key in dropped_element_states {
-                self.element_states.remove(&key);
+                    self.global_action_listeners
+                        .insert(action.as_any().type_id(), global_listeners);
+                }
             }
         }
     }
 
-    fn flush_effects(&mut self) {
-        self.pending_flushes = self.pending_flushes.saturating_sub(1);
-        let mut after_window_update_callbacks = Vec::new();
-
-        if !self.flushing_effects && self.pending_flushes == 0 {
-            self.flushing_effects = true;
+    pub fn has_active_drag(&self) -> bool {
+        self.active_drag.is_some()
+    }
 
-            let mut refreshing = false;
-            let mut updated_windows = HashSet::default();
-            let mut focus_effects = HashMap::<AnyWindowHandle, FocusEffect>::default();
-            loop {
-                self.remove_dropped_entities();
-                if let Some(effect) = self.pending_effects.pop_front() {
-                    match effect {
-                        Effect::Subscription {
-                            entity_id,
-                            subscription_id,
-                            callback,
-                        } => self
-                            .subscriptions
-                            .add_callback(entity_id, subscription_id, callback),
-
-                        Effect::Event { entity_id, payload } => {
-                            let mut subscriptions = self.subscriptions.clone();
-                            subscriptions
-                                .emit(entity_id, |callback| callback(payload.as_ref(), self))
-                        }
+    pub fn active_drag<T: 'static>(&self) -> Option<&T> {
+        self.active_drag
+            .as_ref()
+            .and_then(|drag| drag.value.downcast_ref())
+    }
+}
 
-                        Effect::GlobalSubscription {
-                            type_id,
-                            subscription_id,
-                            callback,
-                        } => self.global_subscriptions.add_callback(
-                            type_id,
-                            subscription_id,
-                            callback,
-                        ),
-
-                        Effect::GlobalEvent { payload } => self.emit_global_event(payload),
-
-                        Effect::Observation {
-                            entity_id,
-                            subscription_id,
-                            callback,
-                        } => self
-                            .observations
-                            .add_callback(entity_id, subscription_id, callback),
-
-                        Effect::ModelNotification { model_id } => {
-                            let mut observations = self.observations.clone();
-                            observations.emit(model_id, |callback| callback(self));
-                        }
+impl Context for AppContext {
+    type Result<T> = T;
 
-                        Effect::ViewNotification {
-                            window: window_id,
-                            view_id,
-                        } => self.handle_view_notification_effect(window_id, view_id),
-
-                        Effect::GlobalNotification { type_id } => {
-                            let mut subscriptions = self.global_observations.clone();
-                            subscriptions.emit(type_id, |callback| {
-                                callback(self);
-                                true
-                            });
-                        }
-
-                        Effect::Deferred {
-                            callback,
-                            after_window_update,
-                        } => {
-                            if after_window_update {
-                                after_window_update_callbacks.push(callback);
-                            } else {
-                                callback(self)
-                            }
-                        }
-
-                        Effect::ModelRelease { model_id, model } => {
-                            self.handle_entity_release_effect(model_id, model.as_any())
-                        }
-
-                        Effect::ViewRelease { view_id, view } => {
-                            self.handle_entity_release_effect(view_id, view.as_any())
-                        }
-
-                        Effect::Focus(mut effect) => {
-                            if focus_effects
-                                .get(&effect.window())
-                                .map_or(false, |prev_effect| prev_effect.is_forced())
-                            {
-                                effect.force();
-                            }
-
-                            focus_effects.insert(effect.window(), effect);
-                        }
-
-                        Effect::FocusObservation {
-                            view_id,
-                            subscription_id,
-                            callback,
-                        } => {
-                            self.focus_observations.add_callback(
-                                view_id,
-                                subscription_id,
-                                callback,
-                            );
-                        }
-
-                        Effect::ResizeWindow { window } => {
-                            if let Some(window) = self.windows.get_mut(&window) {
-                                window
-                                    .invalidation
-                                    .get_or_insert(WindowInvalidation::default());
-                            }
-                            self.handle_window_moved(window);
-                        }
-
-                        Effect::MoveWindow { window } => {
-                            self.handle_window_moved(window);
-                        }
-
-                        Effect::WindowActivationObservation {
-                            window,
-                            subscription_id,
-                            callback,
-                        } => self.window_activation_observations.add_callback(
-                            window,
-                            subscription_id,
-                            callback,
-                        ),
-
-                        Effect::ActivateWindow { window, is_active } => {
-                            if self.handle_window_activation_effect(window, is_active) && is_active
-                            {
-                                focus_effects
-                                    .entry(window)
-                                    .or_insert_with(|| FocusEffect::View {
-                                        window,
-                                        view_id: self
-                                            .read_window(window, |cx| cx.focused_view_id())
-                                            .flatten(),
-                                        is_forced: true,
-                                    })
-                                    .force();
-                            }
-                        }
-
-                        Effect::WindowFullscreenObservation {
-                            window,
-                            subscription_id,
-                            callback,
-                        } => self.window_fullscreen_observations.add_callback(
-                            window,
-                            subscription_id,
-                            callback,
-                        ),
-
-                        Effect::FullscreenWindow {
-                            window,
-                            is_fullscreen,
-                        } => self.handle_fullscreen_effect(window, is_fullscreen),
-
-                        Effect::WindowBoundsObservation {
-                            window,
-                            subscription_id,
-                            callback,
-                        } => self.window_bounds_observations.add_callback(
-                            window,
-                            subscription_id,
-                            callback,
-                        ),
-
-                        Effect::RefreshWindows => {
-                            refreshing = true;
-                        }
-
-                        Effect::ActionDispatchNotification { action_id } => {
-                            self.handle_action_dispatch_notification_effect(action_id)
-                        }
-                        Effect::WindowShouldCloseSubscription { window, callback } => {
-                            self.handle_window_should_close_subscription_effect(window, callback)
-                        }
-                        Effect::Keystroke {
-                            window,
-                            keystroke,
-                            handled_by,
-                            result,
-                        } => self.handle_keystroke_effect(window, keystroke, handled_by, result),
-                        Effect::ActiveLabeledTasksChanged => {
-                            self.handle_active_labeled_tasks_changed_effect()
-                        }
-                        Effect::ActiveLabeledTasksObservation {
-                            subscription_id,
-                            callback,
-                        } => self.active_labeled_task_observations.add_callback(
-                            (),
-                            subscription_id,
-                            callback,
-                        ),
-                        Effect::RepaintWindow { window } => {
-                            self.handle_repaint_window_effect(window)
-                        }
-                    }
-                    self.pending_notifications.clear();
-                } else {
-                    for window in self.windows().collect::<Vec<_>>() {
-                        self.update_window(window, |cx| {
-                            let invalidation = if refreshing {
-                                let mut invalidation =
-                                    cx.window.invalidation.take().unwrap_or_default();
-                                invalidation
-                                    .updated
-                                    .extend(cx.window.rendered_views.keys().copied());
-                                Some(invalidation)
-                            } else {
-                                cx.window.invalidation.take()
-                            };
-
-                            if let Some(invalidation) = invalidation {
-                                let appearance = cx.window.platform_window.appearance();
-                                cx.invalidate(invalidation, appearance);
-                                if let Some(old_parents) = cx.layout(refreshing).log_err() {
-                                    updated_windows.insert(window);
-
-                                    if let Some(focused_view_id) = cx.focused_view_id() {
-                                        let old_ancestors = std::iter::successors(
-                                            Some(focused_view_id),
-                                            |&view_id| old_parents.get(&view_id).copied(),
-                                        )
-                                        .collect::<HashSet<_>>();
-                                        let new_ancestors =
-                                            cx.ancestors(focused_view_id).collect::<HashSet<_>>();
-
-                                        // Notify the old ancestors of the focused view when they don't contain it anymore.
-                                        for old_ancestor in old_ancestors.iter().copied() {
-                                            if !new_ancestors.contains(&old_ancestor) {
-                                                if let Some(mut view) =
-                                                    cx.views.remove(&(window, old_ancestor))
-                                                {
-                                                    view.focus_out(
-                                                        focused_view_id,
-                                                        cx,
-                                                        old_ancestor,
-                                                    );
-                                                    cx.views.insert((window, old_ancestor), view);
-                                                }
-                                            }
-                                        }
-
-                                        // Notify the new ancestors of the focused view if they contain it now.
-                                        for new_ancestor in new_ancestors.iter().copied() {
-                                            if !old_ancestors.contains(&new_ancestor) {
-                                                if let Some(mut view) =
-                                                    cx.views.remove(&(window, new_ancestor))
-                                                {
-                                                    view.focus_in(
-                                                        focused_view_id,
-                                                        cx,
-                                                        new_ancestor,
-                                                    );
-                                                    cx.views.insert((window, new_ancestor), view);
-                                                }
-                                            }
-                                        }
-
-                                        // When the previously-focused view has been dropped and
-                                        // there isn't any pending focus, focus the root view.
-                                        let root_view_id = cx.window.root_view().id();
-                                        if focused_view_id != root_view_id
-                                            && !cx.views.contains_key(&(window, focused_view_id))
-                                            && !focus_effects.contains_key(&window)
-                                        {
-                                            focus_effects.insert(
-                                                window,
-                                                FocusEffect::View {
-                                                    window,
-                                                    view_id: Some(root_view_id),
-                                                    is_forced: false,
-                                                },
-                                            );
-                                        }
-                                    }
-                                }
-                            }
-                        });
-                    }
-
-                    for (_, effect) in focus_effects.drain() {
-                        self.handle_focus_effect(effect);
-                    }
-
-                    if self.pending_effects.is_empty() {
-                        for callback in after_window_update_callbacks.drain(..) {
-                            callback(self);
-                        }
-
-                        for window in updated_windows.drain() {
-                            self.update_window(window, |cx| {
-                                if let Some(scene) = cx.paint().log_err() {
-                                    cx.window.platform_window.present_scene(scene);
-                                }
-                            });
-                        }
-
-                        if self.pending_effects.is_empty() {
-                            self.flushing_effects = false;
-                            self.pending_notifications.clear();
-                            self.pending_global_notifications.clear();
-                            break;
-                        }
-                    }
-
-                    refreshing = false;
-                }
-            }
-        }
-    }
-
-    fn window_was_resized(&mut self, window: AnyWindowHandle) {
-        self.pending_effects
-            .push_back(Effect::ResizeWindow { window });
-    }
-
-    fn window_was_moved(&mut self, window: AnyWindowHandle) {
-        self.pending_effects
-            .push_back(Effect::MoveWindow { window });
-    }
-
-    fn window_was_fullscreen_changed(&mut self, window: AnyWindowHandle, is_fullscreen: bool) {
-        self.pending_effects.push_back(Effect::FullscreenWindow {
-            window,
-            is_fullscreen,
-        });
-    }
-
-    fn window_changed_active_status(&mut self, window: AnyWindowHandle, is_active: bool) {
-        self.pending_effects
-            .push_back(Effect::ActivateWindow { window, is_active });
-    }
-
-    fn keystroke(
-        &mut self,
-        window: AnyWindowHandle,
-        keystroke: Keystroke,
-        handled_by: Option<Box<dyn Action>>,
-        result: MatchResult,
-    ) {
-        self.pending_effects.push_back(Effect::Keystroke {
-            window,
-            keystroke,
-            handled_by,
-            result,
-        });
-    }
-
-    pub fn refresh_windows(&mut self) {
-        self.pending_effects.push_back(Effect::RefreshWindows);
-    }
-
-    fn emit_global_event(&mut self, payload: Box<dyn Any>) {
-        let type_id = (&*payload).type_id();
-
-        let mut subscriptions = self.global_subscriptions.clone();
-        subscriptions.emit(type_id, |callback| {
-            callback(payload.as_ref(), self);
-            true //Always alive
-        });
-    }
-
-    fn handle_view_notification_effect(
-        &mut self,
-        observed_window: AnyWindowHandle,
-        observed_view_id: usize,
-    ) {
-        let view_key = (observed_window, observed_view_id);
-        if let Some((view, mut view_metadata)) = self
-            .views
-            .remove(&view_key)
-            .zip(self.views_metadata.remove(&view_key))
-        {
-            if let Some(window) = self.windows.get_mut(&observed_window) {
-                window
-                    .invalidation
-                    .get_or_insert_with(Default::default)
-                    .updated
-                    .insert(observed_view_id);
-            }
-
-            view.update_keymap_context(&mut view_metadata.keymap_context, self);
-            self.views.insert(view_key, view);
-            self.views_metadata.insert(view_key, view_metadata);
-
-            let mut observations = self.observations.clone();
-            observations.emit(observed_view_id, |callback| callback(self));
-        }
-    }
-
-    fn handle_entity_release_effect(&mut self, entity_id: usize, entity: &dyn Any) {
-        self.release_observations
-            .clone()
-            .emit(entity_id, |callback| {
-                callback(entity, self);
-                // Release observations happen one time. So clear the callback by returning false
-                false
-            })
-    }
-
-    fn handle_fullscreen_effect(&mut self, window: AnyWindowHandle, is_fullscreen: bool) {
-        self.update_window(window, |cx| {
-            cx.window.is_fullscreen = is_fullscreen;
-
-            let mut fullscreen_observations = cx.window_fullscreen_observations.clone();
-            fullscreen_observations.emit(window, |callback| callback(is_fullscreen, cx));
-
-            if let Some(uuid) = cx.window_display_uuid() {
-                let bounds = cx.window_bounds();
-                let mut bounds_observations = cx.window_bounds_observations.clone();
-                bounds_observations.emit(window, |callback| callback(bounds, uuid, cx));
-            }
-
-            Some(())
-        });
-    }
-
-    fn handle_keystroke_effect(
+    /// Build an entity that is owned by the application. The given function will be invoked with
+    /// a `ModelContext` and must return an object representing the entity. A `Model` will be returned
+    /// which can be used to access the entity in a context.
+    fn new_model<T: 'static>(
         &mut self,
-        window: AnyWindowHandle,
-        keystroke: Keystroke,
-        handled_by: Option<Box<dyn Action>>,
-        result: MatchResult,
-    ) {
-        self.update_window(window, |cx| {
-            let mut observations = cx.keystroke_observations.clone();
-            observations.emit(window, move |callback| {
-                callback(&keystroke, &result, handled_by.as_ref(), cx)
-            });
-        });
-    }
-
-    fn handle_repaint_window_effect(&mut self, window: AnyWindowHandle) {
-        self.update_window(window, |cx| {
-            if let Some(scene) = cx.paint().log_err() {
-                cx.window.platform_window.present_scene(scene);
-            }
-        });
-    }
-
-    fn handle_window_activation_effect(&mut self, window: AnyWindowHandle, active: bool) -> bool {
-        self.update_window(window, |cx| {
-            if cx.window.is_active == active {
-                return false;
-            }
-            cx.window.is_active = active;
-
-            let mut observations = cx.window_activation_observations.clone();
-            observations.emit(window, |callback| callback(active, cx));
-            true
+        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
+    ) -> Model<T> {
+        self.update(|cx| {
+            let slot = cx.entities.reserve();
+            let entity = build_model(&mut ModelContext::new(cx, slot.downgrade()));
+            cx.entities.insert(slot, entity)
         })
-        .unwrap_or(false)
-    }
-
-    fn handle_focus_effect(&mut self, effect: FocusEffect) {
-        let window = effect.window();
-        self.update_window(window, |cx| {
-            // Ensure the newly-focused view still exists, otherwise focus
-            // the root view instead.
-            let focused_id = match effect {
-                FocusEffect::View { view_id, .. } => {
-                    if let Some(view_id) = view_id {
-                        if cx.views.contains_key(&(window, view_id)) {
-                            Some(view_id)
-                        } else {
-                            Some(cx.root_view().id())
-                        }
-                    } else {
-                        None
-                    }
-                }
-                FocusEffect::ViewParent { view_id, .. } => Some(
-                    cx.window
-                        .parents
-                        .get(&view_id)
-                        .copied()
-                        .unwrap_or(cx.root_view().id()),
-                ),
-            };
-
-            let focus_changed = cx.window.focused_view_id != focused_id;
-            let blurred_id = cx.window.focused_view_id;
-            cx.window.focused_view_id = focused_id;
-
-            if focus_changed {
-                if let Some(blurred_id) = blurred_id {
-                    for view_id in cx.ancestors(blurred_id).collect::<Vec<_>>() {
-                        if let Some(mut view) = cx.views.remove(&(window, view_id)) {
-                            view.focus_out(blurred_id, cx, view_id);
-                            cx.views.insert((window, view_id), view);
-                        }
-                    }
-
-                    let mut subscriptions = cx.focus_observations.clone();
-                    subscriptions.emit(blurred_id, |callback| callback(false, cx));
-                }
-            }
-
-            if focus_changed || effect.is_forced() {
-                if let Some(focused_id) = focused_id {
-                    for view_id in cx.ancestors(focused_id).collect::<Vec<_>>() {
-                        if let Some(mut view) = cx.views.remove(&(window, view_id)) {
-                            view.focus_in(focused_id, cx, view_id);
-                            cx.views.insert((window, view_id), view);
-                        }
-                    }
-
-                    let mut subscriptions = cx.focus_observations.clone();
-                    subscriptions.emit(focused_id, |callback| callback(true, cx));
-                }
-            }
-        });
-    }
-
-    fn handle_action_dispatch_notification_effect(&mut self, action_id: TypeId) {
-        self.action_dispatch_observations
-            .clone()
-            .emit((), |callback| {
-                callback(action_id, self);
-                true
-            });
     }
 
-    fn handle_window_should_close_subscription_effect(
+    /// Update the entity referenced by the given model. The function is passed a mutable reference to the
+    /// entity along with a `ModelContext` for the entity.
+    fn update_model<T: 'static, R>(
         &mut self,
-        window: AnyWindowHandle,
-        mut callback: WindowShouldCloseSubscriptionCallback,
-    ) {
-        let mut app = self.upgrade();
-        if let Some(window) = self.windows.get_mut(&window) {
-            window
-                .platform_window
-                .on_should_close(Box::new(move || app.update(|cx| callback(cx))))
-        }
-    }
-
-    fn handle_window_moved(&mut self, window: AnyWindowHandle) {
-        self.update_window(window, |cx| {
-            if let Some(display) = cx.window_display_uuid() {
-                let bounds = cx.window_bounds();
-                cx.window_bounds_observations
-                    .clone()
-                    .emit(window, move |callback| {
-                        callback(bounds, display, cx);
-                        true
-                    });
-            }
-        });
-    }
-
-    fn handle_active_labeled_tasks_changed_effect(&mut self) {
-        self.active_labeled_task_observations
-            .clone()
-            .emit((), move |callback| {
-                callback(self);
-                true
-            });
-    }
-
-    pub fn focus(&mut self, window: AnyWindowHandle, view_id: Option<usize>) {
-        self.pending_effects
-            .push_back(Effect::Focus(FocusEffect::View {
-                window,
-                view_id,
-                is_forced: false,
-            }));
+        model: &Model<T>,
+        update: impl FnOnce(&mut T, &mut ModelContext<'_, T>) -> R,
+    ) -> R {
+        self.update(|cx| {
+            let mut entity = cx.entities.lease(model);
+            let result = update(&mut entity, &mut ModelContext::new(cx, model.downgrade()));
+            cx.entities.end_lease(entity);
+            result
+        })
     }
 
-    fn spawn_internal<F, Fut, T>(&mut self, task_name: Option<&'static str>, f: F) -> Task<T>
+    fn update_window<T, F>(&mut self, handle: AnyWindowHandle, update: F) -> Result<T>
     where
-        F: FnOnce(AsyncAppContext) -> Fut,
-        Fut: 'static + Future<Output = T>,
-        T: 'static,
+        F: FnOnce(AnyView, &mut WindowContext<'_>) -> T,
     {
-        let label_id = task_name.map(|task_name| {
-            let id = post_inc(&mut self.next_labeled_task_id);
-            self.active_labeled_tasks.insert(id, task_name);
-            self.pending_effects
-                .push_back(Effect::ActiveLabeledTasksChanged);
-            id
-        });
-
-        let future = f(self.to_async());
-        let cx = self.to_async();
-        self.foreground.spawn(async move {
-            let result = future.await;
-            let mut cx = cx.0.borrow_mut();
-
-            if let Some(completed_label_id) = label_id {
-                cx.active_labeled_tasks.remove(&completed_label_id);
-                cx.pending_effects
-                    .push_back(Effect::ActiveLabeledTasksChanged);
+        self.update(|cx| {
+            let mut window = cx
+                .windows
+                .get_mut(handle.id)
+                .ok_or_else(|| anyhow!("window not found"))?
+                .take()
+                .unwrap();
+
+            let root_view = window.root_view.clone().unwrap();
+            let result = update(root_view, &mut WindowContext::new(cx, &mut window));
+
+            if window.removed {
+                cx.windows.remove(handle.id);
+            } else {
+                cx.windows
+                    .get_mut(handle.id)
+                    .ok_or_else(|| anyhow!("window not found"))?
+                    .replace(window);
             }
-            cx.flush_effects();
-            result
+
+            Ok(result)
         })
     }
 
-    pub fn spawn_labeled<F, Fut, T>(&mut self, task_name: &'static str, f: F) -> Task<T>
+    fn read_model<T, R>(
+        &self,
+        handle: &Model<T>,
+        read: impl FnOnce(&T, &AppContext) -> R,
+    ) -> Self::Result<R>
     where
-        F: FnOnce(AsyncAppContext) -> Fut,
-        Fut: 'static + Future<Output = T>,
         T: 'static,
     {
-        self.spawn_internal(Some(task_name), f)
+        let entity = self.entities.read(handle);
+        read(entity, self)
     }
 
-    pub fn spawn<F, Fut, T>(&mut self, f: F) -> Task<T>
+    fn read_window<T, R>(
+        &self,
+        window: &WindowHandle<T>,
+        read: impl FnOnce(View<T>, &AppContext) -> R,
+    ) -> Result<R>
     where
-        F: FnOnce(AsyncAppContext) -> Fut,
-        Fut: 'static + Future<Output = T>,
         T: 'static,
     {
-        self.spawn_internal(None, f)
-    }
-
-    pub fn to_async(&self) -> AsyncAppContext {
-        AsyncAppContext(self.weak_self.as_ref().unwrap().upgrade().unwrap())
-    }
-
-    pub fn open_url(&self, url: &str) {
-        self.platform.open_url(url)
-    }
-
-    pub fn write_to_clipboard(&self, item: ClipboardItem) {
-        self.platform.write_to_clipboard(item);
-    }
-
-    pub fn read_from_clipboard(&self) -> Option<ClipboardItem> {
-        self.platform.read_from_clipboard()
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn leak_detector(&self) -> Arc<Mutex<LeakDetector>> {
-        self.ref_counts.lock().leak_detector.clone()
-    }
-}
-
-impl BorrowAppContext for AppContext {
-    fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
-        f(self)
-    }
-
-    fn update<T, F: FnOnce(&mut AppContext) -> T>(&mut self, f: F) -> T {
-        f(self)
-    }
-}
-
-impl BorrowWindowContext for AppContext {
-    type Result<T> = Option<T>;
-
-    fn read_window<T, F>(&self, window: AnyWindowHandle, f: F) -> Self::Result<T>
-    where
-        F: FnOnce(&WindowContext) -> T,
-    {
-        AppContext::read_window(self, window, f)
-    }
-
-    fn read_window_optional<T, F>(&self, window: AnyWindowHandle, f: F) -> Option<T>
-    where
-        F: FnOnce(&WindowContext) -> Option<T>,
-    {
-        AppContext::read_window(self, window, f).flatten()
-    }
-
-    fn update_window<T, F>(&mut self, handle: AnyWindowHandle, f: F) -> Self::Result<T>
-    where
-        F: FnOnce(&mut WindowContext) -> T,
-    {
-        self.update(|cx| {
-            let mut window = cx.windows.remove(&handle)?;
-            let mut window_context = WindowContext::mutable(cx, &mut window, handle);
-            let result = f(&mut window_context);
-            if !window_context.removed {
-                cx.windows.insert(handle, window);
-            }
-            Some(result)
-        })
-    }
-
-    fn update_window_optional<T, F>(&mut self, handle: AnyWindowHandle, f: F) -> Option<T>
-    where
-        F: FnOnce(&mut WindowContext) -> Option<T>,
-    {
-        AppContext::update_window(self, handle, f).flatten()
-    }
-}
-
-#[derive(Debug)]
-pub enum ParentId {
-    View(usize),
-    Root,
-}
-
-struct ViewMetadata {
-    type_id: TypeId,
-    keymap_context: KeymapContext,
-}
-
-#[derive(Default, Clone, Debug)]
-pub struct WindowInvalidation {
-    pub updated: HashSet<usize>,
-    pub removed: Vec<usize>,
-}
-
-#[derive(Debug)]
-pub enum FocusEffect {
-    View {
-        window: AnyWindowHandle,
-        view_id: Option<usize>,
-        is_forced: bool,
-    },
-    ViewParent {
-        window: AnyWindowHandle,
-        view_id: usize,
-        is_forced: bool,
-    },
-}
-
-impl FocusEffect {
-    fn window(&self) -> AnyWindowHandle {
-        match self {
-            FocusEffect::View { window, .. } => *window,
-            FocusEffect::ViewParent { window, .. } => *window,
-        }
-    }
+        let window = self
+            .windows
+            .get(window.id)
+            .ok_or_else(|| anyhow!("window not found"))?
+            .as_ref()
+            .unwrap();
 
-    fn is_forced(&self) -> bool {
-        match self {
-            FocusEffect::View { is_forced, .. } => *is_forced,
-            FocusEffect::ViewParent { is_forced, .. } => *is_forced,
-        }
-    }
+        let root_view = window.root_view.clone().unwrap();
+        let view = root_view
+            .downcast::<T>()
+            .map_err(|_| anyhow!("root view's type has changed"))?;
 
-    fn force(&mut self) {
-        match self {
-            FocusEffect::View { is_forced, .. } => *is_forced = true,
-            FocusEffect::ViewParent { is_forced, .. } => *is_forced = true,
-        }
+        Ok(read(view, self))
     }
 }
 
-pub enum Effect {
-    Subscription {
-        entity_id: usize,
-        subscription_id: usize,
-        callback: SubscriptionCallback,
-    },
-    Event {
-        entity_id: usize,
-        payload: Box<dyn Any>,
-    },
-    GlobalSubscription {
-        type_id: TypeId,
-        subscription_id: usize,
-        callback: GlobalSubscriptionCallback,
-    },
-    GlobalEvent {
-        payload: Box<dyn Any>,
-    },
-    Observation {
-        entity_id: usize,
-        subscription_id: usize,
-        callback: ObservationCallback,
-    },
-    ModelNotification {
-        model_id: usize,
-    },
-    ViewNotification {
-        window: AnyWindowHandle,
-        view_id: usize,
-    },
-    Deferred {
-        callback: Box<dyn FnOnce(&mut AppContext)>,
-        after_window_update: bool,
-    },
-    GlobalNotification {
-        type_id: TypeId,
-    },
-    ModelRelease {
-        model_id: usize,
-        model: Box<dyn AnyModel>,
-    },
-    ViewRelease {
-        view_id: usize,
-        view: Box<dyn AnyView>,
-    },
-    Focus(FocusEffect),
-    FocusObservation {
-        view_id: usize,
-        subscription_id: usize,
-        callback: FocusObservationCallback,
+/// These effects are processed at the end of each application update cycle.
+pub(crate) enum Effect {
+    Notify {
+        emitter: EntityId,
     },
-    ResizeWindow {
-        window: AnyWindowHandle,
+    Emit {
+        emitter: EntityId,
+        event_type: TypeId,
+        event: Box<dyn Any>,
     },
-    MoveWindow {
-        window: AnyWindowHandle,
+    Refresh,
+    NotifyGlobalObservers {
+        global_type: TypeId,
     },
-    ActivateWindow {
-        window: AnyWindowHandle,
-        is_active: bool,
-    },
-    RepaintWindow {
-        window: AnyWindowHandle,
-    },
-    WindowActivationObservation {
-        window: AnyWindowHandle,
-        subscription_id: usize,
-        callback: WindowActivationCallback,
-    },
-    FullscreenWindow {
-        window: AnyWindowHandle,
-        is_fullscreen: bool,
-    },
-    WindowFullscreenObservation {
-        window: AnyWindowHandle,
-        subscription_id: usize,
-        callback: WindowFullscreenCallback,
-    },
-    WindowBoundsObservation {
-        window: AnyWindowHandle,
-        subscription_id: usize,
-        callback: WindowBoundsCallback,
-    },
-    Keystroke {
-        window: AnyWindowHandle,
-        keystroke: Keystroke,
-        handled_by: Option<Box<dyn Action>>,
-        result: MatchResult,
-    },
-    RefreshWindows,
-    ActionDispatchNotification {
-        action_id: TypeId,
-    },
-    WindowShouldCloseSubscription {
-        window: AnyWindowHandle,
-        callback: WindowShouldCloseSubscriptionCallback,
-    },
-    ActiveLabeledTasksChanged,
-    ActiveLabeledTasksObservation {
-        subscription_id: usize,
-        callback: ActiveLabeledTasksCallback,
+    Defer {
+        callback: Box<dyn FnOnce(&mut AppContext) + 'static>,
     },
 }
 
-impl Debug for Effect {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Effect::Subscription {
-                entity_id,
-                subscription_id,
-                ..
-            } => f
-                .debug_struct("Effect::Subscribe")
-                .field("entity_id", entity_id)
-                .field("subscription_id", subscription_id)
-                .finish(),
-            Effect::Event { entity_id, .. } => f
-                .debug_struct("Effect::Event")
-                .field("entity_id", entity_id)
-                .finish(),
-            Effect::GlobalSubscription {
-                type_id,
-                subscription_id,
-                ..
-            } => f
-                .debug_struct("Effect::Subscribe")
-                .field("type_id", type_id)
-                .field("subscription_id", subscription_id)
-                .finish(),
-            Effect::GlobalEvent { payload, .. } => f
-                .debug_struct("Effect::GlobalEvent")
-                .field("type_id", &(&*payload).type_id())
-                .finish(),
-            Effect::Observation {
-                entity_id,
-                subscription_id,
-                ..
-            } => f
-                .debug_struct("Effect::Observation")
-                .field("entity_id", entity_id)
-                .field("subscription_id", subscription_id)
-                .finish(),
-            Effect::ModelNotification { model_id } => f
-                .debug_struct("Effect::ModelNotification")
-                .field("model_id", model_id)
-                .finish(),
-            Effect::ViewNotification { window, view_id } => f
-                .debug_struct("Effect::ViewNotification")
-                .field("window_id", &window.id())
-                .field("view_id", view_id)
-                .finish(),
-            Effect::GlobalNotification { type_id } => f
-                .debug_struct("Effect::GlobalNotification")
-                .field("type_id", type_id)
-                .finish(),
-            Effect::Deferred { .. } => f.debug_struct("Effect::Deferred").finish(),
-            Effect::ModelRelease { model_id, .. } => f
-                .debug_struct("Effect::ModelRelease")
-                .field("model_id", model_id)
-                .finish(),
-            Effect::ViewRelease { view_id, .. } => f
-                .debug_struct("Effect::ViewRelease")
-                .field("view_id", view_id)
-                .finish(),
-            Effect::Focus(focus) => f.debug_tuple("Effect::Focus").field(focus).finish(),
-            Effect::FocusObservation {
-                view_id,
-                subscription_id,
-                ..
-            } => f
-                .debug_struct("Effect::FocusObservation")
-                .field("view_id", view_id)
-                .field("subscription_id", subscription_id)
-                .finish(),
-            Effect::ActionDispatchNotification { action_id, .. } => f
-                .debug_struct("Effect::ActionDispatchNotification")
-                .field("action_id", action_id)
-                .finish(),
-            Effect::ResizeWindow { window } => f
-                .debug_struct("Effect::RefreshWindow")
-                .field("window_id", &window.id())
-                .finish(),
-            Effect::MoveWindow { window } => f
-                .debug_struct("Effect::MoveWindow")
-                .field("window_id", &window.id())
-                .finish(),
-            Effect::WindowActivationObservation {
-                window,
-                subscription_id,
-                ..
-            } => f
-                .debug_struct("Effect::WindowActivationObservation")
-                .field("window_id", &window.id())
-                .field("subscription_id", subscription_id)
-                .finish(),
-            Effect::ActivateWindow { window, is_active } => f
-                .debug_struct("Effect::ActivateWindow")
-                .field("window_id", &window.id())
-                .field("is_active", is_active)
-                .finish(),
-            Effect::FullscreenWindow {
-                window,
-                is_fullscreen,
-            } => f
-                .debug_struct("Effect::FullscreenWindow")
-                .field("window_id", &window.id())
-                .field("is_fullscreen", is_fullscreen)
-                .finish(),
-            Effect::WindowFullscreenObservation {
-                window,
-                subscription_id,
-                callback: _,
-            } => f
-                .debug_struct("Effect::WindowFullscreenObservation")
-                .field("window_id", &window.id())
-                .field("subscription_id", subscription_id)
-                .finish(),
-
-            Effect::WindowBoundsObservation {
-                window,
-                subscription_id,
-                callback: _,
-            } => f
-                .debug_struct("Effect::WindowBoundsObservation")
-                .field("window_id", &window.id())
-                .field("subscription_id", subscription_id)
-                .finish(),
-            Effect::RefreshWindows => f.debug_struct("Effect::FullViewRefresh").finish(),
-            Effect::WindowShouldCloseSubscription { window, .. } => f
-                .debug_struct("Effect::WindowShouldCloseSubscription")
-                .field("window_id", &window.id())
-                .finish(),
-            Effect::Keystroke {
-                window,
-                keystroke,
-                handled_by,
-                result,
-            } => f
-                .debug_struct("Effect::Keystroke")
-                .field("window_id", &window.id())
-                .field("keystroke", keystroke)
-                .field(
-                    "keystroke",
-                    &handled_by.as_ref().map(|handled_by| handled_by.name()),
-                )
-                .field("result", result)
-                .finish(),
-            Effect::ActiveLabeledTasksChanged => {
-                f.debug_struct("Effect::ActiveLabeledTasksChanged").finish()
-            }
-            Effect::ActiveLabeledTasksObservation {
-                subscription_id,
-                callback: _,
-            } => f
-                .debug_struct("Effect::ActiveLabeledTasksObservation")
-                .field("subscription_id", subscription_id)
-                .finish(),
-            Effect::RepaintWindow { window } => f
-                .debug_struct("Effect::RepaintWindow")
-                .field("window_id", &window.id())
-                .finish(),
-        }
-    }
+/// Wraps a global variable value during `update_global` while the value has been moved to the stack.
+pub(crate) struct GlobalLease<G: 'static> {
+    global: Box<dyn Any>,
+    global_type: PhantomData<G>,
 }
 
-pub trait AnyModel {
-    fn as_any(&self) -> &dyn Any;
-    fn as_any_mut(&mut self) -> &mut dyn Any;
-    fn release(&mut self, cx: &mut AppContext);
-    fn app_will_quit(
-        &mut self,
-        cx: &mut AppContext,
-    ) -> Option<Pin<Box<dyn 'static + Future<Output = ()>>>>;
+impl<G: 'static> GlobalLease<G> {
+    fn new(global: Box<dyn Any>) -> Self {
+        GlobalLease {
+            global,
+            global_type: PhantomData,
+        }
+    }
 }
 
-impl<T> AnyModel for T
-where
-    T: Entity,
-{
-    fn as_any(&self) -> &dyn Any {
-        self
-    }
+impl<G: 'static> Deref for GlobalLease<G> {
+    type Target = G;
 
-    fn as_any_mut(&mut self) -> &mut dyn Any {
-        self
+    fn deref(&self) -> &Self::Target {
+        self.global.downcast_ref().unwrap()
     }
+}
 
-    fn release(&mut self, cx: &mut AppContext) {
-        self.release(cx);
+impl<G: 'static> DerefMut for GlobalLease<G> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        self.global.downcast_mut().unwrap()
     }
+}
 
-    fn app_will_quit(
-        &mut self,
-        cx: &mut AppContext,
-    ) -> Option<Pin<Box<dyn 'static + Future<Output = ()>>>> {
-        self.app_will_quit(cx)
-    }
+/// Contains state associated with an active drag operation, started by dragging an element
+/// within the window or by dragging into the app from the underlying platform.
+pub struct AnyDrag {
+    pub view: AnyView,
+    pub value: Box<dyn Any>,
+    pub cursor_offset: Point<Pixels>,
 }
 
-pub trait AnyView {
-    fn as_any(&self) -> &dyn Any;
-    fn as_any_mut(&mut self) -> &mut dyn Any;
-    fn release(&mut self, cx: &mut AppContext);
-    fn app_will_quit(
-        &mut self,
-        cx: &mut AppContext,
-    ) -> Option<Pin<Box<dyn 'static + Future<Output = ()>>>>;
-    fn ui_name(&self) -> &'static str;
-    fn render(&mut self, cx: &mut WindowContext, view_id: usize) -> Box<dyn AnyRootElement>;
-    fn focus_in<'a, 'b>(&mut self, focused_id: usize, cx: &mut WindowContext<'a>, view_id: usize);
-    fn focus_out(&mut self, focused_id: usize, cx: &mut WindowContext, view_id: usize);
-    fn key_down(&mut self, event: &KeyDownEvent, cx: &mut WindowContext, view_id: usize) -> bool;
-    fn key_up(&mut self, event: &KeyUpEvent, cx: &mut WindowContext, view_id: usize) -> bool;
-    fn modifiers_changed(
-        &mut self,
-        event: &ModifiersChangedEvent,
-        cx: &mut WindowContext,
-        view_id: usize,
-    ) -> bool;
-    fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext);
-    fn debug_json(&self, cx: &WindowContext) -> serde_json::Value;
-
-    fn text_for_range(&self, range: Range<usize>, cx: &WindowContext) -> Option<String>;
-    fn selected_text_range(&self, cx: &WindowContext) -> Option<Range<usize>>;
-    fn marked_text_range(&self, cx: &WindowContext) -> Option<Range<usize>>;
-    fn unmark_text(&mut self, cx: &mut WindowContext, view_id: usize);
-    fn replace_text_in_range(
-        &mut self,
-        range: Option<Range<usize>>,
-        text: &str,
-        cx: &mut WindowContext,
-        view_id: usize,
-    );
-    fn replace_and_mark_text_in_range(
-        &mut self,
-        range: Option<Range<usize>>,
-        new_text: &str,
-        new_selected_range: Option<Range<usize>>,
-        cx: &mut WindowContext,
-        view_id: usize,
-    );
-    fn any_handle(
-        &self,
-        window: AnyWindowHandle,
-        view_id: usize,
-        cx: &AppContext,
-    ) -> AnyViewHandle {
-        AnyViewHandle::new(
-            window,
-            view_id,
-            self.as_any().type_id(),
-            cx.ref_counts.clone(),
-        )
-    }
+#[derive(Clone)]
+pub(crate) struct AnyTooltip {
+    pub view: AnyView,
+    pub cursor_offset: Point<Pixels>,
 }
 
-impl<V: View> AnyView for V {
-    fn as_any(&self) -> &dyn Any {
-        self
-    }
-
-    fn as_any_mut(&mut self) -> &mut dyn Any {
-        self
-    }
-
-    fn release(&mut self, cx: &mut AppContext) {
-        self.release(cx);
-    }
-
-    fn app_will_quit(
-        &mut self,
-        cx: &mut AppContext,
-    ) -> Option<Pin<Box<dyn 'static + Future<Output = ()>>>> {
-        self.app_will_quit(cx)
-    }
-
-    fn ui_name(&self) -> &'static str {
-        V::ui_name()
-    }
-
-    fn render(&mut self, cx: &mut WindowContext, view_id: usize) -> Box<dyn AnyRootElement> {
-        let mut view_context = ViewContext::mutable(cx, view_id);
-        let element = V::render(self, &mut view_context);
-        let view = WeakViewHandle::new(cx.window_handle, view_id);
-        Box::new(RootElement::new(element, view))
-    }
-
-    fn focus_in(&mut self, focused_id: usize, cx: &mut WindowContext, view_id: usize) {
-        let mut cx = ViewContext::mutable(cx, view_id);
-        let focused_view_handle: AnyViewHandle = if view_id == focused_id {
-            cx.handle().into_any()
-        } else {
-            let focused_type = cx
-                .views_metadata
-                .get(&(cx.window_handle, focused_id))
-                .unwrap()
-                .type_id;
-            AnyViewHandle::new(
-                cx.window_handle,
-                focused_id,
-                focused_type,
-                cx.ref_counts.clone(),
-            )
-        };
-        View::focus_in(self, focused_view_handle, &mut cx);
-    }
-
-    fn focus_out(&mut self, blurred_id: usize, cx: &mut WindowContext, view_id: usize) {
-        let mut cx = ViewContext::mutable(cx, view_id);
-        let blurred_view_handle: AnyViewHandle = if view_id == blurred_id {
-            cx.handle().into_any()
-        } else {
-            let blurred_type = cx
-                .views_metadata
-                .get(&(cx.window_handle, blurred_id))
-                .unwrap()
-                .type_id;
-            AnyViewHandle::new(
-                cx.window_handle,
-                blurred_id,
-                blurred_type,
-                cx.ref_counts.clone(),
-            )
-        };
-        View::focus_out(self, blurred_view_handle, &mut cx);
-    }
-
-    fn key_down(&mut self, event: &KeyDownEvent, cx: &mut WindowContext, view_id: usize) -> bool {
-        let mut cx = ViewContext::mutable(cx, view_id);
-        View::key_down(self, event, &mut cx)
-    }
-
-    fn key_up(&mut self, event: &KeyUpEvent, cx: &mut WindowContext, view_id: usize) -> bool {
-        let mut cx = ViewContext::mutable(cx, view_id);
-        View::key_up(self, event, &mut cx)
-    }
-
-    fn modifiers_changed(
-        &mut self,
-        event: &ModifiersChangedEvent,
-        cx: &mut WindowContext,
-        view_id: usize,
-    ) -> bool {
-        let mut cx = ViewContext::mutable(cx, view_id);
-        View::modifiers_changed(self, event, &mut cx)
-    }
-
-    fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
-        View::update_keymap_context(self, keymap, cx)
-    }
-
-    fn debug_json(&self, cx: &WindowContext) -> serde_json::Value {
-        View::debug_json(self, cx)
-    }
-
-    fn text_for_range(&self, range: Range<usize>, cx: &WindowContext) -> Option<String> {
-        View::text_for_range(self, range, cx)
-    }
-
-    fn selected_text_range(&self, cx: &WindowContext) -> Option<Range<usize>> {
-        View::selected_text_range(self, cx)
-    }
-
-    fn marked_text_range(&self, cx: &WindowContext) -> Option<Range<usize>> {
-        View::marked_text_range(self, cx)
-    }
-
-    fn unmark_text(&mut self, cx: &mut WindowContext, view_id: usize) {
-        let mut cx = ViewContext::mutable(cx, view_id);
-        View::unmark_text(self, &mut cx)
-    }
-
-    fn replace_text_in_range(
-        &mut self,
-        range: Option<Range<usize>>,
-        text: &str,
-        cx: &mut WindowContext,
-        view_id: usize,
-    ) {
-        let mut cx = ViewContext::mutable(cx, view_id);
-        View::replace_text_in_range(self, range, text, &mut cx)
-    }
-
-    fn replace_and_mark_text_in_range(
-        &mut self,
-        range: Option<Range<usize>>,
-        new_text: &str,
-        new_selected_range: Option<Range<usize>>,
-        cx: &mut WindowContext,
-        view_id: usize,
-    ) {
-        let mut cx = ViewContext::mutable(cx, view_id);
-        View::replace_and_mark_text_in_range(self, range, new_text, new_selected_range, &mut cx)
-    }
-}
-
-pub struct ModelContext<'a, T: ?Sized> {
-    app: &'a mut AppContext,
-    model_id: usize,
-    model_type: PhantomData<T>,
-    halt_stream: bool,
-}
-
-impl<'a, T: Entity> ModelContext<'a, T> {
-    fn new(app: &'a mut AppContext, model_id: usize) -> Self {
-        Self {
-            app,
-            model_id,
-            model_type: PhantomData,
-            halt_stream: false,
-        }
-    }
-
-    pub fn background(&self) -> &Arc<executor::Background> {
-        &self.app.background
-    }
-
-    pub fn halt_stream(&mut self) {
-        self.halt_stream = true;
-    }
-
-    pub fn model_id(&self) -> usize {
-        self.model_id
-    }
-
-    pub fn add_model<S, F>(&mut self, build_model: F) -> ModelHandle<S>
-    where
-        S: Entity,
-        F: FnOnce(&mut ModelContext<S>) -> S,
-    {
-        self.app.add_model(build_model)
-    }
-
-    pub fn emit(&mut self, payload: T::Event) {
-        self.app.pending_effects.push_back(Effect::Event {
-            entity_id: self.model_id,
-            payload: Box::new(payload),
-        });
-    }
-
-    pub fn notify(&mut self) {
-        self.app.notify_model(self.model_id);
-    }
-
-    pub fn subscribe<S: Entity, F>(
-        &mut self,
-        handle: &ModelHandle<S>,
-        mut callback: F,
-    ) -> Subscription
-    where
-        S::Event: 'static,
-        F: 'static + FnMut(&mut T, ModelHandle<S>, &S::Event, &mut ModelContext<T>),
-    {
-        let subscriber = self.weak_handle();
-        self.app
-            .subscribe_internal(handle, move |emitter, event, cx| {
-                if let Some(subscriber) = subscriber.upgrade(cx) {
-                    subscriber.update(cx, |subscriber, cx| {
-                        callback(subscriber, emitter, event, cx);
-                    });
-                    true
-                } else {
-                    false
-                }
-            })
-    }
-
-    pub fn observe<S, F>(&mut self, handle: &ModelHandle<S>, mut callback: F) -> Subscription
-    where
-        S: Entity,
-        F: 'static + FnMut(&mut T, ModelHandle<S>, &mut ModelContext<T>),
-    {
-        let observer = self.weak_handle();
-        self.app.observe_internal(handle, move |observed, cx| {
-            if let Some(observer) = observer.upgrade(cx) {
-                observer.update(cx, |observer, cx| {
-                    callback(observer, observed, cx);
-                });
-                true
-            } else {
-                false
-            }
-        })
-    }
-
-    pub fn observe_global<G, F>(&mut self, mut callback: F) -> Subscription
-    where
-        G: Any,
-        F: 'static + FnMut(&mut T, &mut ModelContext<T>),
-    {
-        let observer = self.weak_handle();
-        self.app.observe_global::<G, _>(move |cx| {
-            if let Some(observer) = observer.upgrade(cx) {
-                observer.update(cx, |observer, cx| callback(observer, cx));
-            }
-        })
-    }
-
-    pub fn observe_release<S, F>(
-        &mut self,
-        handle: &ModelHandle<S>,
-        mut callback: F,
-    ) -> Subscription
-    where
-        S: Entity,
-        F: 'static + FnMut(&mut T, &S, &mut ModelContext<T>),
-    {
-        let observer = self.weak_handle();
-        self.app.observe_release(handle, move |released, cx| {
-            if let Some(observer) = observer.upgrade(cx) {
-                observer.update(cx, |observer, cx| {
-                    callback(observer, released, cx);
-                });
-            }
-        })
-    }
-
-    pub fn handle(&self) -> ModelHandle<T> {
-        ModelHandle::new(self.model_id, &self.app.ref_counts)
-    }
-
-    pub fn weak_handle(&self) -> WeakModelHandle<T> {
-        WeakModelHandle::new(self.model_id)
-    }
-
-    pub fn spawn<F, Fut, S>(&mut self, f: F) -> Task<S>
-    where
-        F: FnOnce(ModelHandle<T>, AsyncAppContext) -> Fut,
-        Fut: 'static + Future<Output = S>,
-        S: 'static,
-    {
-        let handle = self.handle();
-        self.app.spawn(|cx| f(handle, cx))
-    }
-
-    pub fn spawn_weak<F, Fut, S>(&mut self, f: F) -> Task<S>
-    where
-        F: FnOnce(WeakModelHandle<T>, AsyncAppContext) -> Fut,
-        Fut: 'static + Future<Output = S>,
-        S: 'static,
-    {
-        let handle = self.weak_handle();
-        self.app.spawn(|cx| f(handle, cx))
-    }
-}
-
-impl<M> AsRef<AppContext> for ModelContext<'_, M> {
-    fn as_ref(&self) -> &AppContext {
-        &self.app
-    }
-}
-
-impl<M> AsMut<AppContext> for ModelContext<'_, M> {
-    fn as_mut(&mut self) -> &mut AppContext {
-        self.app
-    }
-}
-
-impl<M> BorrowAppContext for ModelContext<'_, M> {
-    fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
-        self.app.read_with(f)
-    }
-
-    fn update<T, F: FnOnce(&mut AppContext) -> T>(&mut self, f: F) -> T {
-        self.app.update(f)
-    }
-}
-
-impl<M> Deref for ModelContext<'_, M> {
-    type Target = AppContext;
-
-    fn deref(&self) -> &Self::Target {
-        self.app
-    }
-}
-
-impl<M> DerefMut for ModelContext<'_, M> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.app
-    }
-}
-
-pub struct ViewContext<'a, 'b, T: ?Sized> {
-    window_context: Reference<'b, WindowContext<'a>>,
-    view_id: usize,
-    view_type: PhantomData<T>,
-}
-
-impl<'a, 'b, V> Deref for ViewContext<'a, 'b, V> {
-    type Target = WindowContext<'a>;
-
-    fn deref(&self) -> &Self::Target {
-        &self.window_context
-    }
-}
-
-impl<'a, 'b, V> DerefMut for ViewContext<'a, 'b, V> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.window_context
-    }
-}
-
-impl<'a, 'b, V: 'static> ViewContext<'a, 'b, V> {
-    pub fn mutable(window_context: &'b mut WindowContext<'a>, view_id: usize) -> Self {
-        Self {
-            window_context: Reference::Mutable(window_context),
-            view_id,
-            view_type: PhantomData,
-        }
-    }
-
-    pub fn immutable(window_context: &'b WindowContext<'a>, view_id: usize) -> Self {
-        Self {
-            window_context: Reference::Immutable(window_context),
-            view_id,
-            view_type: PhantomData,
-        }
-    }
-
-    pub fn window_context(&mut self) -> &mut WindowContext<'a> {
-        &mut self.window_context
-    }
-
-    pub fn notify(&mut self) {
-        let window = self.window_handle;
-        let view_id = self.view_id;
-        self.window_context.notify_view(window, view_id);
-    }
-
-    pub fn handle(&self) -> ViewHandle<V> {
-        ViewHandle::new(
-            self.window_handle,
-            self.view_id,
-            &self.window_context.ref_counts,
-        )
-    }
-
-    pub fn weak_handle(&self) -> WeakViewHandle<V> {
-        WeakViewHandle::new(self.window_handle, self.view_id)
-    }
-
-    pub fn window(&self) -> AnyWindowHandle {
-        self.window_handle
-    }
-
-    pub fn view_id(&self) -> usize {
-        self.view_id
-    }
-
-    pub fn foreground(&self) -> &Rc<executor::Foreground> {
-        self.window_context.foreground()
-    }
-
-    pub fn background_executor(&self) -> &Arc<executor::Background> {
-        &self.window_context.background
-    }
-
-    pub fn platform(&self) -> &Arc<dyn Platform> {
-        self.window_context.platform()
-    }
-
-    pub fn prompt_for_paths(
-        &self,
-        options: PathPromptOptions,
-    ) -> oneshot::Receiver<Option<Vec<PathBuf>>> {
-        self.window_context.prompt_for_paths(options)
-    }
-
-    pub fn prompt_for_new_path(&self, directory: &Path) -> oneshot::Receiver<Option<PathBuf>> {
-        self.window_context.prompt_for_new_path(directory)
-    }
-
-    pub fn reveal_path(&self, path: &Path) {
-        self.window_context.reveal_path(path)
-    }
-
-    pub fn focus(&mut self, handle: &AnyViewHandle) {
-        self.window_context.focus(Some(handle.view_id));
-    }
-
-    pub fn focus_self(&mut self) {
-        let view_id = self.view_id;
-        self.window_context.focus(Some(view_id));
-    }
-
-    pub fn is_self_focused(&self) -> bool {
-        self.window.focused_view_id == Some(self.view_id)
-    }
-
-    pub fn focus_parent(&mut self) {
-        let window = self.window_handle;
-        let view_id = self.view_id;
-        self.pending_effects
-            .push_back(Effect::Focus(FocusEffect::ViewParent {
-                window,
-                view_id,
-                is_forced: false,
-            }));
-    }
-
-    pub fn blur(&mut self) {
-        self.window_context.focus(None);
-    }
-
-    pub fn on_window_should_close<F>(&mut self, mut callback: F)
-    where
-        F: 'static + FnMut(&mut V, &mut ViewContext<V>) -> bool,
-    {
-        let window = self.window_handle;
-        let view = self.weak_handle();
-        self.pending_effects
-            .push_back(Effect::WindowShouldCloseSubscription {
-                window,
-                callback: Box::new(move |cx| {
-                    cx.update_window(window, |cx| {
-                        if let Some(view) = view.upgrade(cx) {
-                            view.update(cx, |view, cx| callback(view, cx))
-                        } else {
-                            true
-                        }
-                    })
-                    .unwrap_or(true)
-                }),
-            });
-    }
-
-    pub fn subscribe<E, H, F>(&mut self, handle: &H, mut callback: F) -> Subscription
-    where
-        E: Entity,
-        E::Event: 'static,
-        H: Handle<E>,
-        F: 'static + FnMut(&mut V, H, &E::Event, &mut ViewContext<V>),
-    {
-        let subscriber = self.weak_handle();
-        self.window_context
-            .subscribe_internal(handle, move |emitter, event, cx| {
-                if let Some(subscriber) = subscriber.upgrade(cx) {
-                    subscriber.update(cx, |subscriber, cx| {
-                        callback(subscriber, emitter, event, cx);
-                    });
-                    true
-                } else {
-                    false
-                }
-            })
-    }
-
-    pub fn observe<E, F, H>(&mut self, handle: &H, mut callback: F) -> Subscription
-    where
-        E: Entity,
-        H: Handle<E>,
-        F: 'static + FnMut(&mut V, H, &mut ViewContext<V>),
-    {
-        let window = self.window_handle;
-        let observer = self.weak_handle();
-        self.window_context
-            .observe_internal(handle, move |observed, cx| {
-                cx.update_window(window, |cx| {
-                    if let Some(observer) = observer.upgrade(cx) {
-                        observer.update(cx, |observer, cx| {
-                            callback(observer, observed, cx);
-                        });
-                        true
-                    } else {
-                        false
-                    }
-                })
-                .unwrap_or(false)
-            })
-    }
-
-    pub fn observe_global<G, F>(&mut self, mut callback: F) -> Subscription
-    where
-        G: Any,
-        F: 'static + FnMut(&mut V, &mut ViewContext<V>),
-    {
-        let window = self.window_handle;
-        let observer = self.weak_handle();
-        self.window_context.observe_global::<G, _>(move |cx| {
-            cx.update_window(window, |cx| {
-                if let Some(observer) = observer.upgrade(cx) {
-                    observer.update(cx, |observer, cx| callback(observer, cx));
-                }
-            });
-        })
-    }
-
-    pub fn observe_focus<F, W>(&mut self, handle: &ViewHandle<W>, mut callback: F) -> Subscription
-    where
-        F: 'static + FnMut(&mut V, ViewHandle<W>, bool, &mut ViewContext<V>),
-        W: View,
-    {
-        let observer = self.weak_handle();
-        self.window_context
-            .observe_focus(handle, move |observed, focused, cx| {
-                if let Some(observer) = observer.upgrade(cx) {
-                    observer.update(cx, |observer, cx| {
-                        callback(observer, observed, focused, cx);
-                    });
-                    true
-                } else {
-                    false
-                }
-            })
-    }
-
-    pub fn observe_release<E, F, H>(&mut self, handle: &H, mut callback: F) -> Subscription
-    where
-        E: Entity,
-        H: Handle<E>,
-        F: 'static + FnMut(&mut V, &E, &mut ViewContext<V>),
-    {
-        let window = self.window_handle;
-        let observer = self.weak_handle();
-        self.window_context
-            .observe_release(handle, move |released, cx| {
-                cx.update_window(window, |cx| {
-                    if let Some(observer) = observer.upgrade(cx) {
-                        observer.update(cx, |observer, cx| {
-                            callback(observer, released, cx);
-                        });
-                    }
-                });
-            })
-    }
-
-    pub fn observe_actions<F>(&mut self, mut callback: F) -> Subscription
-    where
-        F: 'static + FnMut(&mut V, TypeId, &mut ViewContext<V>),
-    {
-        let window = self.window_handle;
-        let observer = self.weak_handle();
-        self.window_context.observe_actions(move |action_id, cx| {
-            cx.update_window(window, |cx| {
-                if let Some(observer) = observer.upgrade(cx) {
-                    observer.update(cx, |observer, cx| {
-                        callback(observer, action_id, cx);
-                    });
-                }
-            });
-        })
-    }
-
-    pub fn observe_window_activation<F>(&mut self, mut callback: F) -> Subscription
-    where
-        F: 'static + FnMut(&mut V, bool, &mut ViewContext<V>),
-    {
-        let observer = self.weak_handle();
-        self.window_context
-            .observe_window_activation(move |active, cx| {
-                if let Some(observer) = observer.upgrade(cx) {
-                    observer.update(cx, |observer, cx| {
-                        callback(observer, active, cx);
-                    });
-                    true
-                } else {
-                    false
-                }
-            })
-    }
-
-    pub fn observe_fullscreen<F>(&mut self, mut callback: F) -> Subscription
-    where
-        F: 'static + FnMut(&mut V, bool, &mut ViewContext<V>),
-    {
-        let observer = self.weak_handle();
-        self.window_context.observe_fullscreen(move |active, cx| {
-            if let Some(observer) = observer.upgrade(cx) {
-                observer.update(cx, |observer, cx| {
-                    callback(observer, active, cx);
-                });
-                true
-            } else {
-                false
-            }
-        })
-    }
-
-    pub fn observe_keystrokes<F>(&mut self, mut callback: F) -> Subscription
-    where
-        F: 'static
-            + FnMut(
-                &mut V,
-                &Keystroke,
-                Option<&Box<dyn Action>>,
-                &MatchResult,
-                &mut ViewContext<V>,
-            ) -> bool,
-    {
-        let observer = self.weak_handle();
-        self.window_context
-            .observe_keystrokes(move |keystroke, result, handled_by, cx| {
-                if let Some(observer) = observer.upgrade(cx) {
-                    observer.update(cx, |observer, cx| {
-                        callback(observer, keystroke, handled_by, result, cx);
-                    });
-                    true
-                } else {
-                    false
-                }
-            })
-    }
-
-    pub fn observe_window_bounds<F>(&mut self, mut callback: F) -> Subscription
-    where
-        F: 'static + FnMut(&mut V, WindowBounds, Uuid, &mut ViewContext<V>),
-    {
-        let observer = self.weak_handle();
-        self.window_context
-            .observe_window_bounds(move |bounds, display, cx| {
-                if let Some(observer) = observer.upgrade(cx) {
-                    observer.update(cx, |observer, cx| {
-                        callback(observer, bounds, display, cx);
-                    });
-                    true
-                } else {
-                    false
-                }
-            })
-    }
-
-    pub fn observe_active_labeled_tasks<F>(&mut self, mut callback: F) -> Subscription
-    where
-        F: 'static + FnMut(&mut V, &mut ViewContext<V>),
-    {
-        let window = self.window_handle;
-        let observer = self.weak_handle();
-        self.window_context.observe_active_labeled_tasks(move |cx| {
-            cx.update_window(window, |cx| {
-                if let Some(observer) = observer.upgrade(cx) {
-                    observer.update(cx, |observer, cx| {
-                        callback(observer, cx);
-                    });
-                    true
-                } else {
-                    false
-                }
-            })
-            .unwrap_or(false)
-        })
-    }
-
-    pub fn defer(&mut self, callback: impl 'static + FnOnce(&mut V, &mut ViewContext<V>)) {
-        let handle = self.handle();
-        self.window_context
-            .defer(move |cx| handle.update(cx, |view, cx| callback(view, cx)))
-    }
-
-    pub fn after_window_update(
-        &mut self,
-        callback: impl 'static + FnOnce(&mut V, &mut ViewContext<V>),
-    ) {
-        let window = self.window_handle;
-        let handle = self.handle();
-        self.window_context.after_window_update(move |cx| {
-            cx.update_window(window, |cx| {
-                handle.update(cx, |view, cx| {
-                    callback(view, cx);
-                })
-            });
-        })
-    }
-
-    pub fn propagate_action(&mut self) {
-        self.window_context.halt_action_dispatch = false;
-    }
-
-    pub fn spawn_labeled<F, Fut, S>(&mut self, task_label: &'static str, f: F) -> Task<S>
-    where
-        F: FnOnce(WeakViewHandle<V>, AsyncAppContext) -> Fut,
-        Fut: 'static + Future<Output = S>,
-        S: 'static,
-    {
-        let handle = self.weak_handle();
-        self.window_context
-            .spawn_labeled(task_label, |cx| f(handle, cx))
-    }
-
-    pub fn spawn<F, Fut, S>(&mut self, f: F) -> Task<S>
-    where
-        F: FnOnce(WeakViewHandle<V>, AsyncAppContext) -> Fut,
-        Fut: 'static + Future<Output = S>,
-        S: 'static,
-    {
-        let handle = self.weak_handle();
-        self.window_context.spawn(|cx| f(handle, cx))
-    }
-
-    pub fn mouse_state<Tag: 'static>(&self, region_id: usize) -> MouseState {
-        self.mouse_state_dynamic(TypeTag::new::<Tag>(), region_id)
-    }
-
-    pub fn mouse_state_dynamic(&self, tag: TypeTag, region_id: usize) -> MouseState {
-        let region_id = MouseRegionId::new(tag, self.view_id, region_id);
-        MouseState {
-            hovered: self.window.hovered_region_ids.contains(&region_id),
-            mouse_down: !self.window.clicked_region_ids.is_empty(),
-            clicked: self
-                .window
-                .clicked_region_ids
-                .iter()
-                .find(|click_region_id| **click_region_id == region_id)
-                // If we've gotten here, there should always be a clicked region.
-                // But let's be defensive and return None if there isn't.
-                .and_then(|_| self.window.clicked_region.map(|(_, button)| button)),
-            accessed_hovered: false,
-            accessed_clicked: false,
-        }
-    }
-
-    pub fn element_state<Tag: 'static, T: 'static>(
-        &mut self,
-        element_id: usize,
-        initial: T,
-    ) -> ElementStateHandle<T> {
-        self.element_state_dynamic(TypeTag::new::<Tag>(), element_id, initial)
-    }
-
-    pub fn element_state_dynamic<T: 'static>(
-        &mut self,
-        tag: TypeTag,
-        element_id: usize,
-        initial: T,
-    ) -> ElementStateHandle<T> {
-        let id = ElementStateId {
-            view_id: self.view_id(),
-            element_id,
-            tag,
-        };
-        self.element_states
-            .entry(id)
-            .or_insert_with(|| Box::new(initial));
-        ElementStateHandle::new(id, self.frame_count, &self.ref_counts)
-    }
-
-    pub fn default_element_state<Tag: 'static, T: 'static + Default>(
-        &mut self,
-        element_id: usize,
-    ) -> ElementStateHandle<T> {
-        self.element_state::<Tag, T>(element_id, T::default())
-    }
-
-    pub fn default_element_state_dynamic<T: 'static + Default>(
-        &mut self,
-        tag: TypeTag,
-        element_id: usize,
-    ) -> ElementStateHandle<T> {
-        self.element_state_dynamic::<T>(tag, element_id, T::default())
-    }
-
-    /// Return keystrokes that would dispatch the given action on the given view.
-    pub(crate) fn keystrokes_for_action(
-        &mut self,
-        view_id: usize,
-        action: &dyn Action,
-    ) -> Option<SmallVec<[Keystroke; 2]>> {
-        self.notify_if_view_ancestors_change(view_id);
-
-        let window = self.window_handle;
-        let mut contexts = Vec::new();
-        let mut handler_depth = None;
-        for (i, view_id) in self.ancestors(view_id).enumerate() {
-            if let Some(view_metadata) = self.views_metadata.get(&(window, view_id)) {
-                if let Some(actions) = self.actions.get(&view_metadata.type_id) {
-                    if actions.contains_key(&action.id()) {
-                        handler_depth = Some(i);
-                    }
-                }
-                contexts.push(view_metadata.keymap_context.clone());
-            }
-        }
-
-        if self.global_actions.contains_key(&action.id()) {
-            handler_depth = Some(contexts.len())
-        }
-
-        let handler_depth = handler_depth.unwrap_or(0);
-        (0..=handler_depth).find_map(|depth| {
-            let contexts = &contexts[depth..];
-            self.keystroke_matcher
-                .keystrokes_for_action(action, contexts)
-        })
-    }
-
-    fn notify_if_view_ancestors_change(&mut self, view_id: usize) {
-        let self_view_id = self.view_id;
-        self.window
-            .views_to_notify_if_ancestors_change
-            .entry(view_id)
-            .or_default()
-            .push(self_view_id);
-    }
-
-    pub fn paint_layer<F, R>(&mut self, clip_bounds: Option<RectF>, f: F) -> R
-    where
-        F: FnOnce(&mut Self) -> R,
-    {
-        self.scene().push_layer(clip_bounds);
-        let result = f(self);
-        self.scene().pop_layer();
-        result
-    }
-}
-
-impl<V: View> ViewContext<'_, '_, V> {
-    pub fn emit(&mut self, event: V::Event) {
-        self.window_context
-            .pending_effects
-            .push_back(Effect::Event {
-                entity_id: self.view_id,
-                payload: Box::new(event),
-            });
-    }
-}
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
-pub struct TypeTag {
-    tag: TypeId,
-    composed: Option<TypeId>,
-    #[cfg(debug_assertions)]
-    tag_type_name: &'static str,
-}
-
-impl TypeTag {
-    pub fn new<Tag: 'static>() -> Self {
-        Self {
-            tag: TypeId::of::<Tag>(),
-            composed: None,
-            #[cfg(debug_assertions)]
-            tag_type_name: std::any::type_name::<Tag>(),
-        }
-    }
-
-    pub fn dynamic(tag: TypeId, #[cfg(debug_assertions)] type_name: &'static str) -> Self {
-        Self {
-            tag,
-            composed: None,
-            #[cfg(debug_assertions)]
-            tag_type_name: type_name,
-        }
-    }
-
-    pub fn compose(mut self, other: TypeTag) -> Self {
-        self.composed = Some(other.tag);
-        self
-    }
-
-    #[cfg(debug_assertions)]
-    pub(crate) fn type_name(&self) -> &'static str {
-        self.tag_type_name
-    }
-}
-
-impl<V> BorrowAppContext for ViewContext<'_, '_, V> {
-    fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
-        BorrowAppContext::read_with(&*self.window_context, f)
-    }
-
-    fn update<T, F: FnOnce(&mut AppContext) -> T>(&mut self, f: F) -> T {
-        BorrowAppContext::update(&mut *self.window_context, f)
-    }
-}
-
-impl<V> BorrowWindowContext for ViewContext<'_, '_, V> {
-    type Result<T> = T;
-
-    fn read_window<T, F: FnOnce(&WindowContext) -> T>(&self, window: AnyWindowHandle, f: F) -> T {
-        BorrowWindowContext::read_window(&*self.window_context, window, f)
-    }
-
-    fn read_window_optional<T, F>(&self, window: AnyWindowHandle, f: F) -> Option<T>
-    where
-        F: FnOnce(&WindowContext) -> Option<T>,
-    {
-        BorrowWindowContext::read_window_optional(&*self.window_context, window, f)
-    }
-
-    fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
-        &mut self,
-        window: AnyWindowHandle,
-        f: F,
-    ) -> T {
-        BorrowWindowContext::update_window(&mut *self.window_context, window, f)
-    }
-
-    fn update_window_optional<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Option<T>
-    where
-        F: FnOnce(&mut WindowContext) -> Option<T>,
-    {
-        BorrowWindowContext::update_window_optional(&mut *self.window_context, window, f)
-    }
-}
-
-pub struct EventContext<'a, 'b, 'c, V> {
-    view_context: &'c mut ViewContext<'a, 'b, V>,
-    pub(crate) handled: bool,
-    // I would like to replace handled with this.
-    // Being additive for now.
-    pub bubble: bool,
-}
-
-impl<'a, 'b, 'c, V: 'static> EventContext<'a, 'b, 'c, V> {
-    pub fn new(view_context: &'c mut ViewContext<'a, 'b, V>) -> Self {
-        EventContext {
-            view_context,
-            handled: true,
-            bubble: false,
-        }
-    }
-
-    pub fn propagate_event(&mut self) {
-        self.handled = false;
-    }
-
-    pub fn bubble_event(&mut self) {
-        self.bubble = true;
-    }
-
-    pub fn event_bubbled(&self) -> bool {
-        self.bubble
-    }
-}
-
-impl<'a, 'b, 'c, V> Deref for EventContext<'a, 'b, 'c, V> {
-    type Target = ViewContext<'a, 'b, V>;
-
-    fn deref(&self) -> &Self::Target {
-        &self.view_context
-    }
-}
-
-impl<V> DerefMut for EventContext<'_, '_, '_, V> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.view_context
-    }
-}
-
-impl<V> BorrowAppContext for EventContext<'_, '_, '_, V> {
-    fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
-        BorrowAppContext::read_with(&*self.view_context, f)
-    }
-
-    fn update<T, F: FnOnce(&mut AppContext) -> T>(&mut self, f: F) -> T {
-        BorrowAppContext::update(&mut *self.view_context, f)
-    }
-}
-
-impl<V> BorrowWindowContext for EventContext<'_, '_, '_, V> {
-    type Result<T> = T;
-
-    fn read_window<T, F: FnOnce(&WindowContext) -> T>(&self, window: AnyWindowHandle, f: F) -> T {
-        BorrowWindowContext::read_window(&*self.view_context, window, f)
-    }
-
-    fn read_window_optional<T, F>(&self, window: AnyWindowHandle, f: F) -> Option<T>
-    where
-        F: FnOnce(&WindowContext) -> Option<T>,
-    {
-        BorrowWindowContext::read_window_optional(&*self.view_context, window, f)
-    }
-
-    fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
-        &mut self,
-        window: AnyWindowHandle,
-        f: F,
-    ) -> T {
-        BorrowWindowContext::update_window(&mut *self.view_context, window, f)
-    }
-
-    fn update_window_optional<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Option<T>
-    where
-        F: FnOnce(&mut WindowContext) -> Option<T>,
-    {
-        BorrowWindowContext::update_window_optional(&mut *self.view_context, window, f)
-    }
-}
-
-pub enum Reference<'a, T> {
-    Immutable(&'a T),
-    Mutable(&'a mut T),
-}
-
-impl<'a, T> Deref for Reference<'a, T> {
-    type Target = T;
-
-    fn deref(&self) -> &Self::Target {
-        match self {
-            Reference::Immutable(target) => target,
-            Reference::Mutable(target) => target,
-        }
-    }
-}
-
-impl<'a, T> DerefMut for Reference<'a, T> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        match self {
-            Reference::Immutable(_) => {
-                panic!("cannot mutably deref an immutable reference. this is a bug in GPUI.");
-            }
-            Reference::Mutable(target) => target,
-        }
-    }
-}
-
-#[derive(Debug, Clone, Default)]
-pub struct MouseState {
-    pub(crate) hovered: bool,
-    pub(crate) clicked: Option<MouseButton>,
-    pub(crate) mouse_down: bool,
-    pub(crate) accessed_hovered: bool,
-    pub(crate) accessed_clicked: bool,
-}
-
-impl MouseState {
-    pub fn dragging(&mut self) -> bool {
-        self.accessed_hovered = true;
-        self.hovered && self.mouse_down
-    }
-
-    pub fn hovered(&mut self) -> bool {
-        self.accessed_hovered = true;
-        self.hovered && (!self.mouse_down || self.clicked.is_some())
-    }
-
-    pub fn clicked(&mut self) -> Option<MouseButton> {
-        self.accessed_clicked = true;
-        self.clicked
-    }
-
-    pub fn accessed_hovered(&self) -> bool {
-        self.accessed_hovered
-    }
-
-    pub fn accessed_clicked(&self) -> bool {
-        self.accessed_clicked
-    }
-}
-
-pub trait Handle<T> {
-    type Weak: 'static;
-    fn id(&self) -> usize;
-    fn location(&self) -> EntityLocation;
-    fn downgrade(&self) -> Self::Weak;
-    fn upgrade_from(weak: &Self::Weak, cx: &AppContext) -> Option<Self>
-    where
-        Self: Sized;
-}
-
-pub trait WeakHandle {
-    fn id(&self) -> usize;
-}
-
-#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
-pub enum EntityLocation {
-    Model(usize),
-    View(usize, usize),
-}
-
-pub struct ModelHandle<T: Entity> {
-    any_handle: AnyModelHandle,
-    model_type: PhantomData<T>,
-}
-
-impl<T: Entity> Deref for ModelHandle<T> {
-    type Target = AnyModelHandle;
-
-    fn deref(&self) -> &Self::Target {
-        &self.any_handle
-    }
-}
-
-impl<T: Entity> ModelHandle<T> {
-    fn new(model_id: usize, ref_counts: &Arc<Mutex<RefCounts>>) -> Self {
-        Self {
-            any_handle: AnyModelHandle::new(model_id, TypeId::of::<T>(), ref_counts.clone()),
-            model_type: PhantomData,
-        }
-    }
-
-    pub fn downgrade(&self) -> WeakModelHandle<T> {
-        WeakModelHandle::new(self.model_id)
-    }
-
-    pub fn id(&self) -> usize {
-        self.model_id
-    }
-
-    pub fn read<'a>(&self, cx: &'a AppContext) -> &'a T {
-        cx.read_model(self)
-    }
-
-    pub fn read_with<C, F, S>(&self, cx: &C, read: F) -> S
-    where
-        C: BorrowAppContext,
-        F: FnOnce(&T, &AppContext) -> S,
-    {
-        cx.read_with(|cx| read(self.read(cx), cx))
-    }
-
-    pub fn update<C, F, S>(&self, cx: &mut C, update: F) -> S
-    where
-        C: BorrowAppContext,
-        F: FnOnce(&mut T, &mut ModelContext<T>) -> S,
-    {
-        let mut update = Some(update);
-        cx.update(|cx| {
-            cx.update_model(self, &mut |model, cx| {
-                let update = update.take().unwrap();
-                update(model, cx)
-            })
-        })
-    }
-}
-
-impl<T: Entity> Clone for ModelHandle<T> {
-    fn clone(&self) -> Self {
-        Self::new(self.model_id, &self.ref_counts)
-    }
-}
-
-impl<T: Entity> PartialEq for ModelHandle<T> {
-    fn eq(&self, other: &Self) -> bool {
-        self.model_id == other.model_id
-    }
-}
-
-impl<T: Entity> Eq for ModelHandle<T> {}
-
-impl<T: Entity> PartialEq<WeakModelHandle<T>> for ModelHandle<T> {
-    fn eq(&self, other: &WeakModelHandle<T>) -> bool {
-        self.model_id == other.model_id
-    }
-}
-
-impl<T: Entity> Hash for ModelHandle<T> {
-    fn hash<H: Hasher>(&self, state: &mut H) {
-        self.model_id.hash(state);
-    }
-}
-
-impl<T: Entity> std::borrow::Borrow<usize> for ModelHandle<T> {
-    fn borrow(&self) -> &usize {
-        &self.model_id
-    }
-}
-
-impl<T: Entity> Debug for ModelHandle<T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_tuple(&format!("ModelHandle<{}>", type_name::<T>()))
-            .field(&self.model_id)
-            .finish()
-    }
-}
-
-unsafe impl<T: Entity> Send for ModelHandle<T> {}
-unsafe impl<T: Entity> Sync for ModelHandle<T> {}
-
-impl<T: Entity> Handle<T> for ModelHandle<T> {
-    type Weak = WeakModelHandle<T>;
-
-    fn id(&self) -> usize {
-        self.model_id
-    }
-
-    fn location(&self) -> EntityLocation {
-        EntityLocation::Model(self.model_id)
-    }
-
-    fn downgrade(&self) -> Self::Weak {
-        self.downgrade()
-    }
-
-    fn upgrade_from(weak: &Self::Weak, cx: &AppContext) -> Option<Self>
-    where
-        Self: Sized,
-    {
-        weak.upgrade(cx)
-    }
-}
-
-pub struct WeakModelHandle<T> {
-    any_handle: AnyWeakModelHandle,
-    model_type: PhantomData<T>,
-}
-
-impl<T> WeakModelHandle<T> {
-    pub fn into_any(self) -> AnyWeakModelHandle {
-        self.any_handle
-    }
-}
-
-impl<T> Deref for WeakModelHandle<T> {
-    type Target = AnyWeakModelHandle;
-
-    fn deref(&self) -> &Self::Target {
-        &self.any_handle
-    }
-}
-
-impl<T> WeakHandle for WeakModelHandle<T> {
-    fn id(&self) -> usize {
-        self.model_id
-    }
-}
-
-unsafe impl<T> Send for WeakModelHandle<T> {}
-unsafe impl<T> Sync for WeakModelHandle<T> {}
-
-impl<T: Entity> WeakModelHandle<T> {
-    fn new(model_id: usize) -> Self {
-        Self {
-            any_handle: AnyWeakModelHandle {
-                model_id,
-                model_type: TypeId::of::<T>(),
-            },
-            model_type: PhantomData,
-        }
-    }
-
-    pub fn id(&self) -> usize {
-        self.model_id
-    }
-
-    pub fn is_upgradable(&self, cx: &impl BorrowAppContext) -> bool {
-        cx.read_with(|cx| cx.model_handle_is_upgradable(self))
-    }
-
-    pub fn upgrade(&self, cx: &impl BorrowAppContext) -> Option<ModelHandle<T>> {
-        cx.read_with(|cx| cx.upgrade_model_handle(self))
-    }
-}
-
-impl<T> Hash for WeakModelHandle<T> {
-    fn hash<H: Hasher>(&self, state: &mut H) {
-        self.model_id.hash(state)
-    }
-}
-
-impl<T> PartialEq for WeakModelHandle<T> {
-    fn eq(&self, other: &Self) -> bool {
-        self.model_id == other.model_id
-    }
-}
-
-impl<T> Eq for WeakModelHandle<T> {}
-
-impl<T: Entity> PartialEq<ModelHandle<T>> for WeakModelHandle<T> {
-    fn eq(&self, other: &ModelHandle<T>) -> bool {
-        self.model_id == other.model_id
-    }
-}
-
-impl<T> Clone for WeakModelHandle<T> {
-    fn clone(&self) -> Self {
-        Self {
-            any_handle: self.any_handle.clone(),
-            model_type: PhantomData,
-        }
-    }
-}
-
-impl<T> Copy for WeakModelHandle<T> {}
-
-#[derive(Deref)]
-pub struct WindowHandle<V> {
-    #[deref]
-    any_handle: AnyWindowHandle,
-    root_view_type: PhantomData<V>,
-}
-
-impl<V> Clone for WindowHandle<V> {
-    fn clone(&self) -> Self {
-        Self {
-            any_handle: self.any_handle.clone(),
-            root_view_type: PhantomData,
-        }
-    }
-}
-
-impl<V> Copy for WindowHandle<V> {}
-
-impl<V: 'static> WindowHandle<V> {
-    fn new(window_id: usize) -> Self {
-        WindowHandle {
-            any_handle: AnyWindowHandle::new(window_id, TypeId::of::<V>()),
-            root_view_type: PhantomData,
-        }
-    }
-
-    pub fn root<C: BorrowWindowContext>(&self, cx: &C) -> C::Result<ViewHandle<V>> {
-        self.read_with(cx, |cx| cx.root_view().clone().downcast().unwrap())
-    }
-
-    pub fn read_root_with<C, F, R>(&self, cx: &C, read: F) -> C::Result<R>
-    where
-        C: BorrowWindowContext,
-        F: FnOnce(&V, &ViewContext<V>) -> R,
-    {
-        self.read_with(cx, |cx| {
-            cx.root_view()
-                .downcast_ref::<V>()
-                .unwrap()
-                .read_with(cx, read)
-        })
-    }
-
-    pub fn update_root<C, F, R>(&self, cx: &mut C, update: F) -> C::Result<R>
-    where
-        C: BorrowWindowContext,
-        F: FnOnce(&mut V, &mut ViewContext<V>) -> R,
-    {
-        cx.update_window(self.any_handle, |cx| {
-            cx.root_view()
-                .clone()
-                .downcast::<V>()
-                .unwrap()
-                .update(cx, update)
-        })
-    }
-}
-
-impl<V: View> WindowHandle<V> {
-    pub fn replace_root<C, F>(&self, cx: &mut C, build_root: F) -> C::Result<ViewHandle<V>>
-    where
-        C: BorrowWindowContext,
-        F: FnOnce(&mut ViewContext<V>) -> V,
-    {
-        cx.update_window(self.any_handle, |cx| {
-            let root_view = self.add_view(cx, |cx| build_root(cx));
-            cx.window.root_view = Some(root_view.clone().into_any());
-            cx.window.focused_view_id = Some(root_view.id());
-            root_view
-        })
-    }
-}
-
-impl<V> Into<AnyWindowHandle> for WindowHandle<V> {
-    fn into(self) -> AnyWindowHandle {
-        self.any_handle
-    }
-}
-
-#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
-pub struct AnyWindowHandle {
-    window_id: usize,
-    root_view_type: TypeId,
-}
-
-impl AnyWindowHandle {
-    fn new(window_id: usize, root_view_type: TypeId) -> Self {
-        Self {
-            window_id,
-            root_view_type,
-        }
-    }
-
-    pub fn id(&self) -> usize {
-        self.window_id
-    }
-
-    pub fn read_with<C, F, R>(&self, cx: &C, read: F) -> C::Result<R>
-    where
-        C: BorrowWindowContext,
-        F: FnOnce(&WindowContext) -> R,
-    {
-        cx.read_window(*self, |cx| read(cx))
-    }
-
-    pub fn read_optional_with<C, F, R>(&self, cx: &C, read: F) -> Option<R>
-    where
-        C: BorrowWindowContext,
-        F: FnOnce(&WindowContext) -> Option<R>,
-    {
-        cx.read_window_optional(*self, |cx| read(cx))
-    }
-
-    pub fn update<C, F, R>(&self, cx: &mut C, update: F) -> C::Result<R>
-    where
-        C: BorrowWindowContext,
-        F: FnOnce(&mut WindowContext) -> R,
-    {
-        cx.update_window(*self, update)
-    }
-
-    pub fn update_optional<C, F, R>(&self, cx: &mut C, update: F) -> Option<R>
-    where
-        C: BorrowWindowContext,
-        F: FnOnce(&mut WindowContext) -> Option<R>,
-    {
-        cx.update_window_optional(*self, update)
-    }
-
-    pub fn add_view<C, U, F>(&self, cx: &mut C, build_view: F) -> C::Result<ViewHandle<U>>
-    where
-        C: BorrowWindowContext,
-        U: View,
-        F: FnOnce(&mut ViewContext<U>) -> U,
-    {
-        self.update(cx, |cx| cx.add_view(build_view))
-    }
-
-    pub fn downcast<V: 'static>(self) -> Option<WindowHandle<V>> {
-        if self.root_view_type == TypeId::of::<V>() {
-            Some(WindowHandle {
-                any_handle: self,
-                root_view_type: PhantomData,
-            })
-        } else {
-            None
-        }
-    }
-
-    pub fn root_is<V: 'static>(&self) -> bool {
-        self.root_view_type == TypeId::of::<V>()
-    }
-
-    pub fn is_active<C: BorrowWindowContext>(&self, cx: &C) -> C::Result<bool> {
-        self.read_with(cx, |cx| cx.window.is_active)
-    }
-
-    pub fn remove<C: BorrowWindowContext>(&self, cx: &mut C) -> C::Result<()> {
-        self.update(cx, |cx| cx.remove_window())
-    }
-
-    pub fn debug_elements<C: BorrowWindowContext>(&self, cx: &C) -> Option<json::Value> {
-        self.read_optional_with(cx, |cx| {
-            let root_view = cx.window.root_view();
-            let root_element = cx.window.rendered_views.get(&root_view.id())?;
-            root_element.debug(cx).log_err()
-        })
-    }
-
-    pub fn activate<C: BorrowWindowContext>(&mut self, cx: &mut C) -> C::Result<()> {
-        self.update(cx, |cx| cx.activate_window())
-    }
-
-    pub fn prompt<C: BorrowWindowContext>(
-        &self,
-        level: PromptLevel,
-        msg: &str,
-        answers: &[&str],
-        cx: &mut C,
-    ) -> C::Result<oneshot::Receiver<usize>> {
-        self.update(cx, |cx| cx.prompt(level, msg, answers))
-    }
-
-    pub fn dispatch_action<C: BorrowWindowContext>(
-        &self,
-        view_id: usize,
-        action: &dyn Action,
-        cx: &mut C,
-    ) -> C::Result<()> {
-        self.update(cx, |cx| {
-            cx.dispatch_action(Some(view_id), action);
-        })
-    }
-
-    pub fn available_actions<C: BorrowWindowContext>(
-        &self,
-        view_id: usize,
-        cx: &C,
-    ) -> C::Result<Vec<(&'static str, Box<dyn Action>, SmallVec<[Binding; 1]>)>> {
-        self.read_with(cx, |cx| cx.available_actions(view_id))
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn simulate_activation(&self, cx: &mut TestAppContext) {
-        self.update(cx, |cx| {
-            let other_windows = cx
-                .windows()
-                .filter(|window| *window != *self)
-                .collect::<Vec<_>>();
-
-            for window in other_windows {
-                cx.window_changed_active_status(window, false)
-            }
-
-            cx.window_changed_active_status(*self, true)
-        });
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn simulate_deactivation(&self, cx: &mut TestAppContext) {
-        self.update(cx, |cx| {
-            cx.window_changed_active_status(*self, false);
-        })
-    }
-}
-
-#[repr(transparent)]
-pub struct ViewHandle<V> {
-    any_handle: AnyViewHandle,
-    view_type: PhantomData<V>,
-}
-
-impl<T> Deref for ViewHandle<T> {
-    type Target = AnyViewHandle;
-
-    fn deref(&self) -> &Self::Target {
-        &self.any_handle
-    }
-}
-
-impl<V: 'static> ViewHandle<V> {
-    fn new(window: AnyWindowHandle, view_id: usize, ref_counts: &Arc<Mutex<RefCounts>>) -> Self {
-        Self {
-            any_handle: AnyViewHandle::new(window, view_id, TypeId::of::<V>(), ref_counts.clone()),
-            view_type: PhantomData,
-        }
-    }
-
-    pub fn downgrade(&self) -> WeakViewHandle<V> {
-        WeakViewHandle::new(self.window, self.view_id)
-    }
-
-    pub fn into_any(self) -> AnyViewHandle {
-        self.any_handle
-    }
-
-    pub fn window(&self) -> AnyWindowHandle {
-        self.window
-    }
-
-    pub fn id(&self) -> usize {
-        self.view_id
-    }
-
-    pub fn read<'a>(&self, cx: &'a AppContext) -> &'a V {
-        cx.read_view(self)
-    }
-
-    pub fn read_with<C, F, S>(&self, cx: &C, read: F) -> C::Result<S>
-    where
-        C: BorrowWindowContext,
-        F: FnOnce(&V, &ViewContext<V>) -> S,
-    {
-        cx.read_window(self.window, |cx| {
-            let cx = ViewContext::immutable(cx, self.view_id);
-            read(cx.read_view(self), &cx)
-        })
-    }
-
-    pub fn update<C, F, S>(&self, cx: &mut C, update: F) -> C::Result<S>
-    where
-        C: BorrowWindowContext,
-        F: FnOnce(&mut V, &mut ViewContext<V>) -> S,
-    {
-        let mut update = Some(update);
-
-        cx.update_window(self.window, |cx| {
-            cx.update_view(self, &mut |view, cx| {
-                let update = update.take().unwrap();
-                update(view, cx)
-            })
-        })
-    }
-
-    pub fn is_focused(&self, cx: &WindowContext) -> bool {
-        cx.focused_view_id() == Some(self.view_id)
-    }
-}
-
-impl<T: View> Clone for ViewHandle<T> {
-    fn clone(&self) -> Self {
-        ViewHandle::new(self.window, self.view_id, &self.ref_counts)
-    }
-}
-
-impl<T> PartialEq for ViewHandle<T> {
-    fn eq(&self, other: &Self) -> bool {
-        self.window == other.window && self.view_id == other.view_id
-    }
-}
-
-impl<T> PartialEq<AnyViewHandle> for ViewHandle<T> {
-    fn eq(&self, other: &AnyViewHandle) -> bool {
-        self.window == other.window && self.view_id == other.view_id
-    }
-}
-
-impl<T> PartialEq<WeakViewHandle<T>> for ViewHandle<T> {
-    fn eq(&self, other: &WeakViewHandle<T>) -> bool {
-        self.window == other.window && self.view_id == other.view_id
-    }
-}
-
-impl<T> PartialEq<ViewHandle<T>> for WeakViewHandle<T> {
-    fn eq(&self, other: &ViewHandle<T>) -> bool {
-        self.window == other.window && self.view_id == other.view_id
-    }
-}
-
-impl<T> Eq for ViewHandle<T> {}
-
-impl<T> Hash for ViewHandle<T> {
-    fn hash<H: Hasher>(&self, state: &mut H) {
-        self.window.hash(state);
-        self.view_id.hash(state);
-    }
-}
-
-impl<T> Debug for ViewHandle<T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct(&format!("ViewHandle<{}>", type_name::<T>()))
-            .field("window_id", &self.window)
-            .field("view_id", &self.view_id)
-            .finish()
-    }
-}
-
-impl<T: View> Handle<T> for ViewHandle<T> {
-    type Weak = WeakViewHandle<T>;
-
-    fn id(&self) -> usize {
-        self.view_id
-    }
-
-    fn location(&self) -> EntityLocation {
-        EntityLocation::View(self.window.id(), self.view_id)
-    }
-
-    fn downgrade(&self) -> Self::Weak {
-        self.downgrade()
-    }
-
-    fn upgrade_from(weak: &Self::Weak, cx: &AppContext) -> Option<Self>
-    where
-        Self: Sized,
-    {
-        weak.upgrade(cx)
-    }
-}
-
-pub struct AnyViewHandle {
-    window: AnyWindowHandle,
-    view_id: usize,
-    view_type: TypeId,
-    ref_counts: Arc<Mutex<RefCounts>>,
-
-    #[cfg(any(test, feature = "test-support"))]
-    handle_id: usize,
-}
-
-impl AnyViewHandle {
-    fn new(
-        window: AnyWindowHandle,
-        view_id: usize,
-        view_type: TypeId,
-        ref_counts: Arc<Mutex<RefCounts>>,
-    ) -> Self {
-        ref_counts.lock().inc_view(window, view_id);
-
-        #[cfg(any(test, feature = "test-support"))]
-        let handle_id = ref_counts
-            .lock()
-            .leak_detector
-            .lock()
-            .handle_created(None, view_id);
-
-        Self {
-            window,
-            view_id,
-            view_type,
-            ref_counts,
-            #[cfg(any(test, feature = "test-support"))]
-            handle_id,
-        }
-    }
-
-    pub fn window(&self) -> AnyWindowHandle {
-        self.window
-    }
-
-    pub fn id(&self) -> usize {
-        self.view_id
-    }
-
-    pub fn is<T: 'static>(&self) -> bool {
-        TypeId::of::<T>() == self.view_type
-    }
-
-    pub fn downcast<V: 'static>(self) -> Option<ViewHandle<V>> {
-        if self.is::<V>() {
-            Some(ViewHandle {
-                any_handle: self,
-                view_type: PhantomData,
-            })
-        } else {
-            None
-        }
-    }
-
-    pub fn downcast_ref<V: 'static>(&self) -> Option<&ViewHandle<V>> {
-        if self.is::<V>() {
-            Some(unsafe { mem::transmute(self) })
-        } else {
-            None
-        }
-    }
-
-    pub fn downgrade(&self) -> AnyWeakViewHandle {
-        AnyWeakViewHandle {
-            window: self.window,
-            view_id: self.view_id,
-            view_type: self.view_type,
-        }
-    }
-
-    pub fn view_type(&self) -> TypeId {
-        self.view_type
-    }
-
-    pub fn debug_json<'a, 'b>(&self, cx: &'b WindowContext<'a>) -> serde_json::Value {
-        cx.views
-            .get(&(self.window, self.view_id))
-            .map_or_else(|| serde_json::Value::Null, |view| view.debug_json(cx))
-    }
-}
-
-impl Clone for AnyViewHandle {
-    fn clone(&self) -> Self {
-        Self::new(
-            self.window,
-            self.view_id,
-            self.view_type,
-            self.ref_counts.clone(),
-        )
-    }
-}
-
-impl PartialEq for AnyViewHandle {
-    fn eq(&self, other: &Self) -> bool {
-        self.window == other.window && self.view_id == other.view_id
-    }
-}
-
-impl<T> PartialEq<ViewHandle<T>> for AnyViewHandle {
-    fn eq(&self, other: &ViewHandle<T>) -> bool {
-        self.window == other.window && self.view_id == other.view_id
-    }
-}
-
-impl Drop for AnyViewHandle {
-    fn drop(&mut self) {
-        self.ref_counts.lock().dec_view(self.window, self.view_id);
-        #[cfg(any(test, feature = "test-support"))]
-        self.ref_counts
-            .lock()
-            .leak_detector
-            .lock()
-            .handle_dropped(self.view_id, self.handle_id);
-    }
-}
-
-impl Debug for AnyViewHandle {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("AnyViewHandle")
-            .field("window_id", &self.window.id())
-            .field("view_id", &self.view_id)
-            .finish()
-    }
-}
-
-pub struct AnyModelHandle {
-    model_id: usize,
-    model_type: TypeId,
-    ref_counts: Arc<Mutex<RefCounts>>,
-
-    #[cfg(any(test, feature = "test-support"))]
-    handle_id: usize,
-}
-
-impl AnyModelHandle {
-    fn new(model_id: usize, model_type: TypeId, ref_counts: Arc<Mutex<RefCounts>>) -> Self {
-        ref_counts.lock().inc_model(model_id);
-
-        #[cfg(any(test, feature = "test-support"))]
-        let handle_id = ref_counts
-            .lock()
-            .leak_detector
-            .lock()
-            .handle_created(None, model_id);
-
-        Self {
-            model_id,
-            model_type,
-            ref_counts,
-
-            #[cfg(any(test, feature = "test-support"))]
-            handle_id,
-        }
-    }
-
-    pub fn downcast<T: Entity>(self) -> Option<ModelHandle<T>> {
-        if self.is::<T>() {
-            Some(ModelHandle {
-                any_handle: self,
-                model_type: PhantomData,
-            })
-        } else {
-            None
-        }
-    }
-
-    pub fn downgrade(&self) -> AnyWeakModelHandle {
-        AnyWeakModelHandle {
-            model_id: self.model_id,
-            model_type: self.model_type,
-        }
-    }
-
-    pub fn is<T: Entity>(&self) -> bool {
-        self.model_type == TypeId::of::<T>()
-    }
-
-    pub fn model_type(&self) -> TypeId {
-        self.model_type
-    }
-}
-
-impl Clone for AnyModelHandle {
-    fn clone(&self) -> Self {
-        Self::new(self.model_id, self.model_type, self.ref_counts.clone())
-    }
-}
-
-impl Drop for AnyModelHandle {
-    fn drop(&mut self) {
-        let mut ref_counts = self.ref_counts.lock();
-        ref_counts.dec_model(self.model_id);
-
-        #[cfg(any(test, feature = "test-support"))]
-        ref_counts
-            .leak_detector
-            .lock()
-            .handle_dropped(self.model_id, self.handle_id);
-    }
-}
-
-#[derive(Hash, PartialEq, Eq, Debug, Clone, Copy)]
-pub struct AnyWeakModelHandle {
-    model_id: usize,
-    model_type: TypeId,
-}
-
-impl AnyWeakModelHandle {
-    pub fn upgrade(&self, cx: &impl BorrowAppContext) -> Option<AnyModelHandle> {
-        cx.read_with(|cx| cx.upgrade_any_model_handle(self))
-    }
-
-    pub fn model_type(&self) -> TypeId {
-        self.model_type
-    }
-
-    fn is<T: 'static>(&self) -> bool {
-        TypeId::of::<T>() == self.model_type
-    }
-
-    pub fn downcast<T: Entity>(self) -> Option<WeakModelHandle<T>> {
-        if self.is::<T>() {
-            let result = Some(WeakModelHandle {
-                any_handle: self,
-                model_type: PhantomData,
-            });
-
-            result
-        } else {
-            None
-        }
-    }
-}
-
-pub struct WeakViewHandle<T> {
-    any_handle: AnyWeakViewHandle,
-    view_type: PhantomData<T>,
-}
-
-impl<T> Copy for WeakViewHandle<T> {}
-
-impl<T> Debug for WeakViewHandle<T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct(&format!("WeakViewHandle<{}>", type_name::<T>()))
-            .field("any_handle", &self.any_handle)
-            .finish()
-    }
-}
-
-impl<T> WeakHandle for WeakViewHandle<T> {
-    fn id(&self) -> usize {
-        self.view_id
-    }
-}
-
-impl<V: 'static> WeakViewHandle<V> {
-    fn new(window: AnyWindowHandle, view_id: usize) -> Self {
-        Self {
-            any_handle: AnyWeakViewHandle {
-                window,
-                view_id,
-                view_type: TypeId::of::<V>(),
-            },
-            view_type: PhantomData,
-        }
-    }
-
-    pub fn id(&self) -> usize {
-        self.view_id
-    }
-
-    pub fn window(&self) -> AnyWindowHandle {
-        self.window
-    }
-
-    pub fn window_id(&self) -> usize {
-        self.window.id()
-    }
-
-    pub fn into_any(self) -> AnyWeakViewHandle {
-        self.any_handle
-    }
-
-    pub fn upgrade(&self, cx: &impl BorrowAppContext) -> Option<ViewHandle<V>> {
-        cx.read_with(|cx| cx.upgrade_view_handle(self))
-    }
-
-    pub fn read_with<T>(
-        &self,
-        cx: &AsyncAppContext,
-        read: impl FnOnce(&V, &ViewContext<V>) -> T,
-    ) -> Result<T> {
-        cx.read(|cx| {
-            let handle = cx
-                .upgrade_view_handle(self)
-                .ok_or_else(|| anyhow!("view was dropped"))?;
-            cx.read_window(self.window, |cx| handle.read_with(cx, read))
-                .ok_or_else(|| anyhow!("window was removed"))
-        })
-    }
-
-    pub fn update<T, B>(
-        &self,
-        cx: &mut B,
-        update: impl FnOnce(&mut V, &mut ViewContext<V>) -> T,
-    ) -> Result<T>
-    where
-        B: BorrowWindowContext,
-        B::Result<Option<T>>: Flatten<T>,
-    {
-        cx.update_window(self.window(), |cx| {
-            cx.upgrade_view_handle(self)
-                .map(|handle| handle.update(cx, update))
-        })
-        .flatten()
-        .ok_or_else(|| anyhow!("window was removed"))
-    }
-}
-
-pub trait Flatten<T> {
-    fn flatten(self) -> Option<T>;
-}
-
-impl<T> Flatten<T> for Option<Option<T>> {
-    fn flatten(self) -> Option<T> {
-        self.flatten()
-    }
-}
-
-impl<T> Flatten<T> for Option<T> {
-    fn flatten(self) -> Option<T> {
-        self
-    }
-}
-
-impl<V> Deref for WeakViewHandle<V> {
-    type Target = AnyWeakViewHandle;
-
-    fn deref(&self) -> &Self::Target {
-        &self.any_handle
-    }
-}
-
-impl<V> Clone for WeakViewHandle<V> {
-    fn clone(&self) -> Self {
-        Self {
-            any_handle: self.any_handle.clone(),
-            view_type: PhantomData,
-        }
-    }
-}
-
-impl<T> PartialEq for WeakViewHandle<T> {
-    fn eq(&self, other: &Self) -> bool {
-        self.window == other.window && self.view_id == other.view_id
-    }
-}
-
-impl<T> Eq for WeakViewHandle<T> {}
-
-impl<T> Hash for WeakViewHandle<T> {
-    fn hash<H: Hasher>(&self, state: &mut H) {
-        self.any_handle.hash(state);
-    }
-}
-
-#[derive(Debug, Clone, Copy, Eq, PartialEq)]
-pub struct AnyWeakViewHandle {
-    window: AnyWindowHandle,
-    view_id: usize,
-    view_type: TypeId,
-}
-
-impl AnyWeakViewHandle {
-    pub fn id(&self) -> usize {
-        self.view_id
-    }
-
-    fn is<T: 'static>(&self) -> bool {
-        TypeId::of::<T>() == self.view_type
-    }
-
-    pub fn upgrade(&self, cx: &impl BorrowAppContext) -> Option<AnyViewHandle> {
-        cx.read_with(|cx| cx.upgrade_any_view_handle(self))
-    }
-
-    pub fn downcast<T: View>(self) -> Option<WeakViewHandle<T>> {
-        if self.is::<T>() {
-            Some(WeakViewHandle {
-                any_handle: self,
-                view_type: PhantomData,
-            })
-        } else {
-            None
-        }
-    }
-}
-
-impl Hash for AnyWeakViewHandle {
-    fn hash<H: Hasher>(&self, state: &mut H) {
-        self.window.hash(state);
-        self.view_id.hash(state);
-        self.view_type.hash(state);
-    }
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub struct ElementStateId {
-    view_id: usize,
-    element_id: usize,
-    tag: TypeTag,
-}
-
-pub struct ElementStateHandle<T> {
-    value_type: PhantomData<T>,
-    id: ElementStateId,
-    ref_counts: Weak<Mutex<RefCounts>>,
-}
-
-impl<T: 'static> ElementStateHandle<T> {
-    fn new(id: ElementStateId, frame_id: usize, ref_counts: &Arc<Mutex<RefCounts>>) -> Self {
-        ref_counts.lock().inc_element_state(id, frame_id);
-        Self {
-            value_type: PhantomData,
-            id,
-            ref_counts: Arc::downgrade(ref_counts),
-        }
-    }
-
-    pub fn id(&self) -> ElementStateId {
-        self.id
-    }
-
-    pub fn read<'a>(&self, cx: &'a AppContext) -> &'a T {
-        cx.element_states
-            .get(&self.id)
-            .unwrap()
-            .downcast_ref()
-            .unwrap()
-    }
-
-    pub fn update<C, D, R>(&self, cx: &mut C, f: impl FnOnce(&mut T, &mut C) -> R) -> R
-    where
-        C: DerefMut<Target = D>,
-        D: DerefMut<Target = AppContext>,
-    {
-        let mut element_state = cx.deref_mut().element_states.remove(&self.id).unwrap();
-        let result = f(element_state.downcast_mut().unwrap(), cx);
-        cx.deref_mut().element_states.insert(self.id, element_state);
-        result
-    }
-}
-
-impl<T> Drop for ElementStateHandle<T> {
-    fn drop(&mut self) {
-        if let Some(ref_counts) = self.ref_counts.upgrade() {
-            ref_counts.lock().dec_element_state(self.id);
-        }
-    }
-}
-
-#[must_use]
-pub enum Subscription {
-    Subscription(callback_collection::Subscription<usize, SubscriptionCallback>),
-    Observation(callback_collection::Subscription<usize, ObservationCallback>),
-    GlobalSubscription(callback_collection::Subscription<TypeId, GlobalSubscriptionCallback>),
-    GlobalObservation(callback_collection::Subscription<TypeId, GlobalObservationCallback>),
-    FocusObservation(callback_collection::Subscription<usize, FocusObservationCallback>),
-    WindowActivationObservation(
-        callback_collection::Subscription<AnyWindowHandle, WindowActivationCallback>,
-    ),
-    WindowFullscreenObservation(
-        callback_collection::Subscription<AnyWindowHandle, WindowFullscreenCallback>,
-    ),
-    WindowBoundsObservation(
-        callback_collection::Subscription<AnyWindowHandle, WindowBoundsCallback>,
-    ),
-    KeystrokeObservation(callback_collection::Subscription<AnyWindowHandle, KeystrokeCallback>),
-    ReleaseObservation(callback_collection::Subscription<usize, ReleaseObservationCallback>),
-    ActionObservation(callback_collection::Subscription<(), ActionObservationCallback>),
-    ActiveLabeledTasksObservation(
-        callback_collection::Subscription<(), ActiveLabeledTasksCallback>,
-    ),
-}
-
-impl Subscription {
-    pub fn id(&self) -> usize {
-        match self {
-            Subscription::Subscription(subscription) => subscription.id(),
-            Subscription::Observation(subscription) => subscription.id(),
-            Subscription::GlobalSubscription(subscription) => subscription.id(),
-            Subscription::GlobalObservation(subscription) => subscription.id(),
-            Subscription::FocusObservation(subscription) => subscription.id(),
-            Subscription::WindowActivationObservation(subscription) => subscription.id(),
-            Subscription::WindowFullscreenObservation(subscription) => subscription.id(),
-            Subscription::WindowBoundsObservation(subscription) => subscription.id(),
-            Subscription::KeystrokeObservation(subscription) => subscription.id(),
-            Subscription::ReleaseObservation(subscription) => subscription.id(),
-            Subscription::ActionObservation(subscription) => subscription.id(),
-            Subscription::ActiveLabeledTasksObservation(subscription) => subscription.id(),
-        }
-    }
-
-    pub fn detach(&mut self) {
-        match self {
-            Subscription::Subscription(subscription) => subscription.detach(),
-            Subscription::GlobalSubscription(subscription) => subscription.detach(),
-            Subscription::Observation(subscription) => subscription.detach(),
-            Subscription::GlobalObservation(subscription) => subscription.detach(),
-            Subscription::FocusObservation(subscription) => subscription.detach(),
-            Subscription::KeystrokeObservation(subscription) => subscription.detach(),
-            Subscription::WindowActivationObservation(subscription) => subscription.detach(),
-            Subscription::WindowFullscreenObservation(subscription) => subscription.detach(),
-            Subscription::WindowBoundsObservation(subscription) => subscription.detach(),
-            Subscription::ReleaseObservation(subscription) => subscription.detach(),
-            Subscription::ActionObservation(subscription) => subscription.detach(),
-            Subscription::ActiveLabeledTasksObservation(subscription) => subscription.detach(),
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::{
-        actions,
-        elements::*,
-        impl_actions,
-        platform::{MouseButton, MouseButtonEvent},
-        window::ChildView,
-    };
-    use itertools::Itertools;
-    use postage::{sink::Sink, stream::Stream};
-    use serde::Deserialize;
-    use smol::future::poll_once;
-    use std::{
-        cell::Cell,
-        sync::atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst},
-    };
-
-    #[crate::test(self)]
-    fn test_model_handles(cx: &mut AppContext) {
-        struct Model {
-            other: Option<ModelHandle<Model>>,
-            events: Vec<String>,
-        }
-
-        impl Entity for Model {
-            type Event = usize;
-        }
-
-        impl Model {
-            fn new(other: Option<ModelHandle<Self>>, cx: &mut ModelContext<Self>) -> Self {
-                if let Some(other) = other.as_ref() {
-                    cx.observe(other, |me, _, _| {
-                        me.events.push("notified".into());
-                    })
-                    .detach();
-                    cx.subscribe(other, |me, _, event, _| {
-                        me.events.push(format!("observed event {}", event));
-                    })
-                    .detach();
-                }
-
-                Self {
-                    other,
-                    events: Vec::new(),
-                }
-            }
-        }
-
-        let handle_1 = cx.add_model(|cx| Model::new(None, cx));
-        let handle_2 = cx.add_model(|cx| Model::new(Some(handle_1.clone()), cx));
-        assert_eq!(cx.models.len(), 2);
-
-        handle_1.update(cx, |model, cx| {
-            model.events.push("updated".into());
-            cx.emit(1);
-            cx.notify();
-            cx.emit(2);
-        });
-        assert_eq!(handle_1.read(cx).events, vec!["updated".to_string()]);
-        assert_eq!(
-            handle_2.read(cx).events,
-            vec![
-                "observed event 1".to_string(),
-                "notified".to_string(),
-                "observed event 2".to_string(),
-            ]
-        );
-
-        handle_2.update(cx, |model, _| {
-            drop(handle_1);
-            model.other.take();
-        });
-
-        assert_eq!(cx.models.len(), 1);
-        assert!(cx.subscriptions.is_empty());
-        assert!(cx.observations.is_empty());
-    }
-
-    #[crate::test(self)]
-    fn test_model_events(cx: &mut AppContext) {
-        #[derive(Default)]
-        struct Model {
-            events: Vec<usize>,
-        }
-
-        impl Entity for Model {
-            type Event = usize;
-        }
-
-        let handle_1 = cx.add_model(|_| Model::default());
-        let handle_2 = cx.add_model(|_| Model::default());
-
-        handle_1.update(cx, |_, cx| {
-            cx.subscribe(&handle_2, move |model: &mut Model, emitter, event, cx| {
-                model.events.push(*event);
-
-                cx.subscribe(&emitter, |model, _, event, _| {
-                    model.events.push(*event * 2);
-                })
-                .detach();
-            })
-            .detach();
-        });
-
-        handle_2.update(cx, |_, c| c.emit(7));
-        assert_eq!(handle_1.read(cx).events, vec![7]);
-
-        handle_2.update(cx, |_, c| c.emit(5));
-        assert_eq!(handle_1.read(cx).events, vec![7, 5, 10]);
-    }
-
-    #[crate::test(self)]
-    fn test_model_emit_before_subscribe_in_same_update_cycle(cx: &mut AppContext) {
-        #[derive(Default)]
-        struct Model;
-
-        impl Entity for Model {
-            type Event = ();
-        }
-
-        let events = Rc::new(RefCell::new(Vec::new()));
-        cx.add_model(|cx| {
-            drop(cx.subscribe(&cx.handle(), {
-                let events = events.clone();
-                move |_, _, _, _| events.borrow_mut().push("dropped before flush")
-            }));
-            cx.subscribe(&cx.handle(), {
-                let events = events.clone();
-                move |_, _, _, _| events.borrow_mut().push("before emit")
-            })
-            .detach();
-            cx.emit(());
-            cx.subscribe(&cx.handle(), {
-                let events = events.clone();
-                move |_, _, _, _| events.borrow_mut().push("after emit")
-            })
-            .detach();
-            Model
-        });
-        assert_eq!(*events.borrow(), ["before emit"]);
-    }
-
-    #[crate::test(self)]
-    fn test_observe_and_notify_from_model(cx: &mut AppContext) {
-        #[derive(Default)]
-        struct Model {
-            count: usize,
-            events: Vec<usize>,
-        }
-
-        impl Entity for Model {
-            type Event = ();
-        }
-
-        let handle_1 = cx.add_model(|_| Model::default());
-        let handle_2 = cx.add_model(|_| Model::default());
-
-        handle_1.update(cx, |_, c| {
-            c.observe(&handle_2, move |model, observed, c| {
-                model.events.push(observed.read(c).count);
-                c.observe(&observed, |model, observed, c| {
-                    model.events.push(observed.read(c).count * 2);
-                })
-                .detach();
-            })
-            .detach();
-        });
-
-        handle_2.update(cx, |model, c| {
-            model.count = 7;
-            c.notify()
-        });
-        assert_eq!(handle_1.read(cx).events, vec![7]);
-
-        handle_2.update(cx, |model, c| {
-            model.count = 5;
-            c.notify()
-        });
-        assert_eq!(handle_1.read(cx).events, vec![7, 5, 10])
-    }
-
-    #[crate::test(self)]
-    fn test_model_notify_before_observe_in_same_update_cycle(cx: &mut AppContext) {
-        #[derive(Default)]
-        struct Model;
-
-        impl Entity for Model {
-            type Event = ();
-        }
-
-        let events = Rc::new(RefCell::new(Vec::new()));
-        cx.add_model(|cx| {
-            drop(cx.observe(&cx.handle(), {
-                let events = events.clone();
-                move |_, _, _| events.borrow_mut().push("dropped before flush")
-            }));
-            cx.observe(&cx.handle(), {
-                let events = events.clone();
-                move |_, _, _| events.borrow_mut().push("before notify")
-            })
-            .detach();
-            cx.notify();
-            cx.observe(&cx.handle(), {
-                let events = events.clone();
-                move |_, _, _| events.borrow_mut().push("after notify")
-            })
-            .detach();
-            Model
-        });
-        assert_eq!(*events.borrow(), ["before notify"]);
-    }
-
-    #[crate::test(self)]
-    fn test_defer_and_after_window_update(cx: &mut TestAppContext) {
-        struct View {
-            render_count: usize,
-        }
-
-        impl Entity for View {
-            type Event = usize;
-        }
-
-        impl super::View for View {
-            fn render(&mut self, _: &mut ViewContext<Self>) -> AnyElement<Self> {
-                post_inc(&mut self.render_count);
-                Empty::new().into_any()
-            }
-
-            fn ui_name() -> &'static str {
-                "View"
-            }
-        }
-
-        let window = cx.add_window(|_| View { render_count: 0 });
-        let called_defer = Rc::new(AtomicBool::new(false));
-        let called_after_window_update = Rc::new(AtomicBool::new(false));
-
-        window.root(cx).update(cx, |this, cx| {
-            assert_eq!(this.render_count, 1);
-            cx.defer({
-                let called_defer = called_defer.clone();
-                move |this, _| {
-                    assert_eq!(this.render_count, 1);
-                    called_defer.store(true, SeqCst);
-                }
-            });
-            cx.after_window_update({
-                let called_after_window_update = called_after_window_update.clone();
-                move |this, cx| {
-                    assert_eq!(this.render_count, 2);
-                    called_after_window_update.store(true, SeqCst);
-                    cx.notify();
-                }
-            });
-            assert!(!called_defer.load(SeqCst));
-            assert!(!called_after_window_update.load(SeqCst));
-            cx.notify();
-        });
-
-        assert!(called_defer.load(SeqCst));
-        assert!(called_after_window_update.load(SeqCst));
-        assert_eq!(window.read_root_with(cx, |view, _| view.render_count), 3);
-    }
-
-    #[crate::test(self)]
-    fn test_view_handles(cx: &mut TestAppContext) {
-        struct View {
-            other: Option<ViewHandle<View>>,
-            events: Vec<String>,
-        }
-
-        impl Entity for View {
-            type Event = usize;
-        }
-
-        impl super::View for View {
-            fn render(&mut self, _: &mut ViewContext<Self>) -> AnyElement<Self> {
-                Empty::new().into_any()
-            }
-
-            fn ui_name() -> &'static str {
-                "View"
-            }
-        }
-
-        impl View {
-            fn new(other: Option<ViewHandle<View>>, cx: &mut ViewContext<Self>) -> Self {
-                if let Some(other) = other.as_ref() {
-                    cx.subscribe(other, |me, _, event, _| {
-                        me.events.push(format!("observed event {}", event));
-                    })
-                    .detach();
-                }
-                Self {
-                    other,
-                    events: Vec::new(),
-                }
-            }
-        }
-
-        let window = cx.add_window(|cx| View::new(None, cx));
-        let handle_1 = window.add_view(cx, |cx| View::new(None, cx));
-        let handle_2 = window.add_view(cx, |cx| View::new(Some(handle_1.clone()), cx));
-        assert_eq!(cx.read(|cx| cx.views.len()), 3);
-
-        handle_1.update(cx, |view, cx| {
-            view.events.push("updated".into());
-            cx.emit(1);
-            cx.emit(2);
-        });
-        handle_1.read_with(cx, |view, _| {
-            assert_eq!(view.events, vec!["updated".to_string()]);
-        });
-        handle_2.read_with(cx, |view, _| {
-            assert_eq!(
-                view.events,
-                vec![
-                    "observed event 1".to_string(),
-                    "observed event 2".to_string(),
-                ]
-            );
-        });
-
-        handle_2.update(cx, |view, _| {
-            drop(handle_1);
-            view.other.take();
-        });
-
-        cx.read(|cx| {
-            assert_eq!(cx.views.len(), 2);
-            assert!(cx.subscriptions.is_empty());
-            assert!(cx.observations.is_empty());
-        });
-    }
-
-    #[crate::test(self)]
-    fn test_add_window(cx: &mut AppContext) {
-        struct View {
-            mouse_down_count: Arc<AtomicUsize>,
-        }
-
-        impl Entity for View {
-            type Event = ();
-        }
-
-        impl super::View for View {
-            fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
-                enum Handler {}
-                let mouse_down_count = self.mouse_down_count.clone();
-                MouseEventHandler::new::<Handler, _>(0, cx, |_, _| Empty::new())
-                    .on_down(MouseButton::Left, move |_, _, _| {
-                        mouse_down_count.fetch_add(1, SeqCst);
-                    })
-                    .into_any()
-            }
-
-            fn ui_name() -> &'static str {
-                "View"
-            }
-        }
-
-        let mouse_down_count = Arc::new(AtomicUsize::new(0));
-        let window = cx.add_window(Default::default(), |_| View {
-            mouse_down_count: mouse_down_count.clone(),
-        });
-
-        window.update(cx, |cx| {
-            // Ensure window's root element is in a valid lifecycle state.
-            cx.dispatch_event(
-                Event::MouseDown(MouseButtonEvent {
-                    position: Default::default(),
-                    button: MouseButton::Left,
-                    modifiers: Default::default(),
-                    click_count: 1,
-                    is_down: true,
-                }),
-                false,
-            );
-            assert_eq!(mouse_down_count.load(SeqCst), 1);
-        });
-    }
-
-    #[crate::test(self)]
-    fn test_entity_release_hooks(cx: &mut TestAppContext) {
-        struct Model {
-            released: Rc<Cell<bool>>,
-        }
-
-        struct View {
-            released: Rc<Cell<bool>>,
-        }
-
-        impl Entity for Model {
-            type Event = ();
-
-            fn release(&mut self, _: &mut AppContext) {
-                self.released.set(true);
-            }
-        }
-
-        impl Entity for View {
-            type Event = ();
-
-            fn release(&mut self, _: &mut AppContext) {
-                self.released.set(true);
-            }
-        }
-
-        impl super::View for View {
-            fn ui_name() -> &'static str {
-                "View"
-            }
-
-            fn render(&mut self, _: &mut ViewContext<Self>) -> AnyElement<Self> {
-                Empty::new().into_any()
-            }
-        }
-
-        let model_released = Rc::new(Cell::new(false));
-        let model_release_observed = Rc::new(Cell::new(false));
-        let view_released = Rc::new(Cell::new(false));
-        let view_release_observed = Rc::new(Cell::new(false));
-
-        let model = cx.add_model(|_| Model {
-            released: model_released.clone(),
-        });
-        let window = cx.add_window(|_| View {
-            released: view_released.clone(),
-        });
-        let view = window.root(cx);
-
-        assert!(!model_released.get());
-        assert!(!view_released.get());
-
-        cx.update(|cx| {
-            cx.observe_release(&model, {
-                let model_release_observed = model_release_observed.clone();
-                move |_, _| model_release_observed.set(true)
-            })
-            .detach();
-            cx.observe_release(&view, {
-                let view_release_observed = view_release_observed.clone();
-                move |_, _| view_release_observed.set(true)
-            })
-            .detach();
-        });
-
-        cx.update(move |_| {
-            drop(model);
-        });
-        assert!(model_released.get());
-        assert!(model_release_observed.get());
-
-        drop(view);
-        window.update(cx, |cx| cx.remove_window());
-        assert!(view_released.get());
-        assert!(view_release_observed.get());
-    }
-
-    #[crate::test(self)]
-    fn test_view_events(cx: &mut TestAppContext) {
-        struct Model;
-
-        impl Entity for Model {
-            type Event = String;
-        }
-
-        let window = cx.add_window(|_| TestView::default());
-        let handle_1 = window.root(cx);
-        let handle_2 = window.add_view(cx, |_| TestView::default());
-        let handle_3 = cx.add_model(|_| Model);
-
-        handle_1.update(cx, |_, cx| {
-            cx.subscribe(&handle_2, move |me, emitter, event, cx| {
-                me.events.push(event.clone());
-
-                cx.subscribe(&emitter, |me, _, event, _| {
-                    me.events.push(format!("{event} from inner"));
-                })
-                .detach();
-            })
-            .detach();
-
-            cx.subscribe(&handle_3, |me, _, event, _| {
-                me.events.push(event.clone());
-            })
-            .detach();
-        });
-
-        handle_2.update(cx, |_, c| c.emit("7".into()));
-        handle_1.read_with(cx, |view, _| assert_eq!(view.events, ["7"]));
-
-        handle_2.update(cx, |_, c| c.emit("5".into()));
-        handle_1.read_with(cx, |view, _| {
-            assert_eq!(view.events, ["7", "5", "5 from inner"])
-        });
-
-        handle_3.update(cx, |_, c| c.emit("9".into()));
-        handle_1.read_with(cx, |view, _| {
-            assert_eq!(view.events, ["7", "5", "5 from inner", "9"])
-        });
-    }
-
-    #[crate::test(self)]
-    fn test_global_events(cx: &mut AppContext) {
-        #[derive(Clone, Debug, Eq, PartialEq)]
-        struct GlobalEvent(u64);
-
-        let events = Rc::new(RefCell::new(Vec::new()));
-        let first_subscription;
-        let second_subscription;
-
-        {
-            let events = events.clone();
-            first_subscription = cx.subscribe_global(move |e: &GlobalEvent, _| {
-                events.borrow_mut().push(("First", e.clone()));
-            });
-        }
-
-        {
-            let events = events.clone();
-            second_subscription = cx.subscribe_global(move |e: &GlobalEvent, _| {
-                events.borrow_mut().push(("Second", e.clone()));
-            });
-        }
-
-        cx.update(|cx| {
-            cx.emit_global(GlobalEvent(1));
-            cx.emit_global(GlobalEvent(2));
-        });
-
-        drop(first_subscription);
-
-        cx.update(|cx| {
-            cx.emit_global(GlobalEvent(3));
-        });
-
-        drop(second_subscription);
-
-        cx.update(|cx| {
-            cx.emit_global(GlobalEvent(4));
-        });
-
-        assert_eq!(
-            &*events.borrow(),
-            &[
-                ("First", GlobalEvent(1)),
-                ("Second", GlobalEvent(1)),
-                ("First", GlobalEvent(2)),
-                ("Second", GlobalEvent(2)),
-                ("Second", GlobalEvent(3)),
-            ]
-        );
-    }
-
-    #[crate::test(self)]
-    fn test_global_events_emitted_before_subscription_in_same_update_cycle(cx: &mut AppContext) {
-        let events = Rc::new(RefCell::new(Vec::new()));
-        cx.update(|cx| {
-            {
-                let events = events.clone();
-                drop(cx.subscribe_global(move |_: &(), _| {
-                    events.borrow_mut().push("dropped before emit");
-                }));
-            }
-
-            {
-                let events = events.clone();
-                cx.subscribe_global(move |_: &(), _| {
-                    events.borrow_mut().push("before emit");
-                })
-                .detach();
-            }
-
-            cx.emit_global(());
-
-            {
-                let events = events.clone();
-                cx.subscribe_global(move |_: &(), _| {
-                    events.borrow_mut().push("after emit");
-                })
-                .detach();
-            }
-        });
-
-        assert_eq!(*events.borrow(), ["before emit"]);
-    }
-
-    #[crate::test(self)]
-    fn test_global_nested_events(cx: &mut AppContext) {
-        #[derive(Clone, Debug, Eq, PartialEq)]
-        struct GlobalEvent(u64);
-
-        let events = Rc::new(RefCell::new(Vec::new()));
-
-        {
-            let events = events.clone();
-            cx.subscribe_global(move |e: &GlobalEvent, cx| {
-                events.borrow_mut().push(("Outer", e.clone()));
-
-                if e.0 == 1 {
-                    let events = events.clone();
-                    cx.subscribe_global(move |e: &GlobalEvent, _| {
-                        events.borrow_mut().push(("Inner", e.clone()));
-                    })
-                    .detach();
-                }
-            })
-            .detach();
-        }
-
-        cx.update(|cx| {
-            cx.emit_global(GlobalEvent(1));
-            cx.emit_global(GlobalEvent(2));
-            cx.emit_global(GlobalEvent(3));
-        });
-        cx.update(|cx| {
-            cx.emit_global(GlobalEvent(4));
-        });
-
-        assert_eq!(
-            &*events.borrow(),
-            &[
-                ("Outer", GlobalEvent(1)),
-                ("Outer", GlobalEvent(2)),
-                ("Outer", GlobalEvent(3)),
-                ("Outer", GlobalEvent(4)),
-                ("Inner", GlobalEvent(4)),
-            ]
-        );
-    }
-
-    #[crate::test(self)]
-    fn test_global(cx: &mut AppContext) {
-        type Global = usize;
-
-        let observation_count = Rc::new(RefCell::new(0));
-        let subscription = cx.observe_global::<Global, _>({
-            let observation_count = observation_count.clone();
-            move |_| {
-                *observation_count.borrow_mut() += 1;
-            }
-        });
-
-        assert!(!cx.has_global::<Global>());
-        assert_eq!(cx.default_global::<Global>(), &0);
-        assert_eq!(*observation_count.borrow(), 1);
-        assert!(cx.has_global::<Global>());
-        assert_eq!(
-            cx.update_global::<Global, _, _>(|global, _| {
-                *global = 1;
-                "Update Result"
-            }),
-            "Update Result"
-        );
-        assert_eq!(*observation_count.borrow(), 2);
-        assert_eq!(cx.global::<Global>(), &1);
-
-        drop(subscription);
-        cx.update_global::<Global, _, _>(|global, _| {
-            *global = 2;
-        });
-        assert_eq!(*observation_count.borrow(), 2);
-
-        type OtherGlobal = f32;
-
-        let observation_count = Rc::new(RefCell::new(0));
-        cx.observe_global::<OtherGlobal, _>({
-            let observation_count = observation_count.clone();
-            move |_| {
-                *observation_count.borrow_mut() += 1;
-            }
-        })
-        .detach();
-
-        assert_eq!(
-            cx.update_default_global::<OtherGlobal, _, _>(|global, _| {
-                assert_eq!(global, &0.0);
-                *global = 2.0;
-                "Default update result"
-            }),
-            "Default update result"
-        );
-        assert_eq!(cx.global::<OtherGlobal>(), &2.0);
-        assert_eq!(*observation_count.borrow(), 1);
-    }
-
-    #[crate::test(self)]
-    fn test_dropping_subscribers(cx: &mut TestAppContext) {
-        struct Model;
-
-        impl Entity for Model {
-            type Event = ();
-        }
-
-        let window = cx.add_window(|_| TestView::default());
-        let observing_view = window.add_view(cx, |_| TestView::default());
-        let emitting_view = window.add_view(cx, |_| TestView::default());
-        let observing_model = cx.add_model(|_| Model);
-        let observed_model = cx.add_model(|_| Model);
-
-        observing_view.update(cx, |_, cx| {
-            cx.subscribe(&emitting_view, |_, _, _, _| {}).detach();
-            cx.subscribe(&observed_model, |_, _, _, _| {}).detach();
-        });
-        observing_model.update(cx, |_, cx| {
-            cx.subscribe(&observed_model, |_, _, _, _| {}).detach();
-        });
-
-        cx.update(|_| {
-            drop(observing_view);
-            drop(observing_model);
-        });
-
-        emitting_view.update(cx, |_, cx| cx.emit(Default::default()));
-        observed_model.update(cx, |_, cx| cx.emit(()));
-    }
-
-    #[crate::test(self)]
-    fn test_view_emit_before_subscribe_in_same_update_cycle(cx: &mut AppContext) {
-        let window = cx.add_window::<TestView, _>(Default::default(), |cx| {
-            drop(cx.subscribe(&cx.handle(), {
-                move |this, _, _, _| this.events.push("dropped before flush".into())
-            }));
-            cx.subscribe(&cx.handle(), {
-                move |this, _, _, _| this.events.push("before emit".into())
-            })
-            .detach();
-            cx.emit("the event".into());
-            cx.subscribe(&cx.handle(), {
-                move |this, _, _, _| this.events.push("after emit".into())
-            })
-            .detach();
-            TestView { events: Vec::new() }
-        });
-
-        window.read_root_with(cx, |view, _| assert_eq!(view.events, ["before emit"]));
-    }
-
-    #[crate::test(self)]
-    fn test_observe_and_notify_from_view(cx: &mut TestAppContext) {
-        #[derive(Default)]
-        struct Model {
-            state: String,
-        }
-
-        impl Entity for Model {
-            type Event = ();
-        }
-
-        let window = cx.add_window(|_| TestView::default());
-        let view = window.root(cx);
-        let model = cx.add_model(|_| Model {
-            state: "old-state".into(),
-        });
-
-        view.update(cx, |_, c| {
-            c.observe(&model, |me, observed, cx| {
-                me.events.push(observed.read(cx).state.clone())
-            })
-            .detach();
-        });
-
-        model.update(cx, |model, cx| {
-            model.state = "new-state".into();
-            cx.notify();
-        });
-        view.read_with(cx, |view, _| assert_eq!(view.events, ["new-state"]));
-    }
-
-    #[crate::test(self)]
-    fn test_view_notify_before_observe_in_same_update_cycle(cx: &mut AppContext) {
-        let window = cx.add_window::<TestView, _>(Default::default(), |cx| {
-            drop(cx.observe(&cx.handle(), {
-                move |this, _, _| this.events.push("dropped before flush".into())
-            }));
-            cx.observe(&cx.handle(), {
-                move |this, _, _| this.events.push("before notify".into())
-            })
-            .detach();
-            cx.notify();
-            cx.observe(&cx.handle(), {
-                move |this, _, _| this.events.push("after notify".into())
-            })
-            .detach();
-            TestView { events: Vec::new() }
-        });
-
-        window.read_root_with(cx, |view, _| assert_eq!(view.events, ["before notify"]));
-    }
-
-    #[crate::test(self)]
-    fn test_notify_and_drop_observe_subscription_in_same_update_cycle(cx: &mut TestAppContext) {
-        struct Model;
-        impl Entity for Model {
-            type Event = ();
-        }
-
-        let model = cx.add_model(|_| Model);
-        let window = cx.add_window(|_| TestView::default());
-        let view = window.root(cx);
-
-        view.update(cx, |_, cx| {
-            model.update(cx, |_, cx| cx.notify());
-            drop(cx.observe(&model, move |this, _, _| {
-                this.events.push("model notified".into());
-            }));
-            model.update(cx, |_, cx| cx.notify());
-        });
-
-        for _ in 0..3 {
-            model.update(cx, |_, cx| cx.notify());
-        }
-        view.read_with(cx, |view, _| assert_eq!(view.events, Vec::<&str>::new()));
-    }
-
-    #[crate::test(self)]
-    fn test_dropping_observers(cx: &mut TestAppContext) {
-        struct Model;
-
-        impl Entity for Model {
-            type Event = ();
-        }
-
-        let window = cx.add_window(|_| TestView::default());
-        let observing_view = window.add_view(cx, |_| TestView::default());
-        let observing_model = cx.add_model(|_| Model);
-        let observed_model = cx.add_model(|_| Model);
-
-        observing_view.update(cx, |_, cx| {
-            cx.observe(&observed_model, |_, _, _| {}).detach();
-        });
-        observing_model.update(cx, |_, cx| {
-            cx.observe(&observed_model, |_, _, _| {}).detach();
-        });
-
-        cx.update(|_| {
-            drop(observing_view);
-            drop(observing_model);
-        });
-
-        observed_model.update(cx, |_, cx| cx.notify());
-    }
-
-    #[crate::test(self)]
-    fn test_dropping_subscriptions_during_callback(cx: &mut TestAppContext) {
-        struct Model;
-
-        impl Entity for Model {
-            type Event = u64;
-        }
-
-        // Events
-        let observing_model = cx.add_model(|_| Model);
-        let observed_model = cx.add_model(|_| Model);
-
-        let events = Rc::new(RefCell::new(Vec::new()));
-
-        observing_model.update(cx, |_, cx| {
-            let events = events.clone();
-            let subscription = Rc::new(RefCell::new(None));
-            *subscription.borrow_mut() = Some(cx.subscribe(&observed_model, {
-                let subscription = subscription.clone();
-                move |_, _, e, _| {
-                    subscription.borrow_mut().take();
-                    events.borrow_mut().push(*e);
-                }
-            }));
-        });
-
-        observed_model.update(cx, |_, cx| {
-            cx.emit(1);
-            cx.emit(2);
-        });
-
-        assert_eq!(*events.borrow(), [1]);
-
-        // Global Events
-        #[derive(Clone, Debug, Eq, PartialEq)]
-        struct GlobalEvent(u64);
-
-        let events = Rc::new(RefCell::new(Vec::new()));
-
-        {
-            let events = events.clone();
-            let subscription = Rc::new(RefCell::new(None));
-            *subscription.borrow_mut() = Some(cx.subscribe_global({
-                let subscription = subscription.clone();
-                move |e: &GlobalEvent, _| {
-                    subscription.borrow_mut().take();
-                    events.borrow_mut().push(e.clone());
-                }
-            }));
-        }
-
-        cx.update(|cx| {
-            cx.emit_global(GlobalEvent(1));
-            cx.emit_global(GlobalEvent(2));
-        });
-
-        assert_eq!(*events.borrow(), [GlobalEvent(1)]);
-
-        // Model Observation
-        let observing_model = cx.add_model(|_| Model);
-        let observed_model = cx.add_model(|_| Model);
-
-        let observation_count = Rc::new(RefCell::new(0));
-
-        observing_model.update(cx, |_, cx| {
-            let observation_count = observation_count.clone();
-            let subscription = Rc::new(RefCell::new(None));
-            *subscription.borrow_mut() = Some(cx.observe(&observed_model, {
-                let subscription = subscription.clone();
-                move |_, _, _| {
-                    subscription.borrow_mut().take();
-                    *observation_count.borrow_mut() += 1;
-                }
-            }));
-        });
-
-        observed_model.update(cx, |_, cx| {
-            cx.notify();
-        });
-
-        observed_model.update(cx, |_, cx| {
-            cx.notify();
-        });
-
-        assert_eq!(*observation_count.borrow(), 1);
-
-        // View Observation
-        struct View;
-
-        impl Entity for View {
-            type Event = ();
-        }
-
-        impl super::View for View {
-            fn render(&mut self, _: &mut ViewContext<Self>) -> AnyElement<Self> {
-                Empty::new().into_any()
-            }
-
-            fn ui_name() -> &'static str {
-                "View"
-            }
-        }
-
-        let window = cx.add_window(|_| View);
-        let observing_view = window.add_view(cx, |_| View);
-        let observed_view = window.add_view(cx, |_| View);
-
-        let observation_count = Rc::new(RefCell::new(0));
-        observing_view.update(cx, |_, cx| {
-            let observation_count = observation_count.clone();
-            let subscription = Rc::new(RefCell::new(None));
-            *subscription.borrow_mut() = Some(cx.observe(&observed_view, {
-                let subscription = subscription.clone();
-                move |_, _, _| {
-                    subscription.borrow_mut().take();
-                    *observation_count.borrow_mut() += 1;
-                }
-            }));
-        });
-
-        observed_view.update(cx, |_, cx| {
-            cx.notify();
-        });
-
-        observed_view.update(cx, |_, cx| {
-            cx.notify();
-        });
-
-        assert_eq!(*observation_count.borrow(), 1);
-
-        // Global Observation
-        let observation_count = Rc::new(RefCell::new(0));
-        let subscription = Rc::new(RefCell::new(None));
-        *subscription.borrow_mut() = Some(cx.observe_global::<(), _>({
-            let observation_count = observation_count.clone();
-            let subscription = subscription.clone();
-            move |_| {
-                subscription.borrow_mut().take();
-                *observation_count.borrow_mut() += 1;
-            }
-        }));
-
-        cx.update(|cx| {
-            cx.default_global::<()>();
-            cx.set_global(());
-        });
-        assert_eq!(*observation_count.borrow(), 1);
-    }
-
-    #[crate::test(self)]
-    fn test_focus(cx: &mut TestAppContext) {
-        struct View {
-            name: String,
-            events: Arc<Mutex<Vec<String>>>,
-            child: Option<AnyViewHandle>,
-        }
-
-        impl Entity for View {
-            type Event = ();
-        }
-
-        impl super::View for View {
-            fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
-                self.child
-                    .as_ref()
-                    .map(|child| ChildView::new(child, cx).into_any())
-                    .unwrap_or(Empty::new().into_any())
-            }
-
-            fn ui_name() -> &'static str {
-                "View"
-            }
-
-            fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
-                if cx.handle().id() == focused.id() {
-                    self.events.lock().push(format!("{} focused", &self.name));
-                }
-            }
-
-            fn focus_out(&mut self, blurred: AnyViewHandle, cx: &mut ViewContext<Self>) {
-                if cx.handle().id() == blurred.id() {
-                    self.events.lock().push(format!("{} blurred", &self.name));
-                }
-            }
-        }
-
-        let view_events: Arc<Mutex<Vec<String>>> = Default::default();
-        let window = cx.add_window(|_| View {
-            events: view_events.clone(),
-            name: "view 1".to_string(),
-            child: None,
-        });
-        let view_1 = window.root(cx);
-        let view_2 = window.update(cx, |cx| {
-            let view_2 = cx.add_view(|_| View {
-                events: view_events.clone(),
-                name: "view 2".to_string(),
-                child: None,
-            });
-            view_1.update(cx, |view_1, cx| {
-                view_1.child = Some(view_2.clone().into_any());
-                cx.notify();
-            });
-            view_2
-        });
-
-        let observed_events: Arc<Mutex<Vec<String>>> = Default::default();
-        view_1.update(cx, |_, cx| {
-            cx.observe_focus(&view_2, {
-                let observed_events = observed_events.clone();
-                move |this, view, focused, cx| {
-                    let label = if focused { "focus" } else { "blur" };
-                    observed_events.lock().push(format!(
-                        "{} observed {}'s {}",
-                        this.name,
-                        view.read(cx).name,
-                        label
-                    ))
-                }
-            })
-            .detach();
-        });
-        view_2.update(cx, |_, cx| {
-            cx.observe_focus(&view_1, {
-                let observed_events = observed_events.clone();
-                move |this, view, focused, cx| {
-                    let label = if focused { "focus" } else { "blur" };
-                    observed_events.lock().push(format!(
-                        "{} observed {}'s {}",
-                        this.name,
-                        view.read(cx).name,
-                        label
-                    ))
-                }
-            })
-            .detach();
-        });
-        assert_eq!(mem::take(&mut *view_events.lock()), ["view 1 focused"]);
-        assert_eq!(mem::take(&mut *observed_events.lock()), Vec::<&str>::new());
-
-        view_1.update(cx, |_, cx| {
-            // Ensure only the last focus event is honored.
-            cx.focus(&view_2);
-            cx.focus(&view_1);
-            cx.focus(&view_2);
-        });
-
-        assert_eq!(
-            mem::take(&mut *view_events.lock()),
-            ["view 1 blurred", "view 2 focused"],
-        );
-        assert_eq!(
-            mem::take(&mut *observed_events.lock()),
-            [
-                "view 2 observed view 1's blur",
-                "view 1 observed view 2's focus"
-            ]
-        );
-
-        view_1.update(cx, |_, cx| cx.focus(&view_1));
-        assert_eq!(
-            mem::take(&mut *view_events.lock()),
-            ["view 2 blurred", "view 1 focused"],
-        );
-        assert_eq!(
-            mem::take(&mut *observed_events.lock()),
-            [
-                "view 1 observed view 2's blur",
-                "view 2 observed view 1's focus"
-            ]
-        );
-
-        view_1.update(cx, |_, cx| cx.focus(&view_2));
-        assert_eq!(
-            mem::take(&mut *view_events.lock()),
-            ["view 1 blurred", "view 2 focused"],
-        );
-        assert_eq!(
-            mem::take(&mut *observed_events.lock()),
-            [
-                "view 2 observed view 1's blur",
-                "view 1 observed view 2's focus"
-            ]
-        );
-
-        println!("=====================");
-        view_1.update(cx, |view, _| {
-            drop(view_2);
-            view.child = None;
-        });
-        assert_eq!(mem::take(&mut *view_events.lock()), ["view 1 focused"]);
-        assert_eq!(mem::take(&mut *observed_events.lock()), Vec::<&str>::new());
-    }
-
-    #[crate::test(self)]
-    fn test_deserialize_actions(cx: &mut AppContext) {
-        #[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
-        pub struct ComplexAction {
-            arg: String,
-            count: usize,
-        }
-
-        actions!(test::something, [SimpleAction]);
-        impl_actions!(test::something, [ComplexAction]);
-
-        cx.add_global_action(move |_: &SimpleAction, _: &mut AppContext| {});
-        cx.add_global_action(move |_: &ComplexAction, _: &mut AppContext| {});
-
-        let action1 = cx
-            .deserialize_action(
-                "test::something::ComplexAction",
-                Some(serde_json::from_str(r#"{"arg": "a", "count": 5}"#).unwrap()),
-            )
-            .unwrap();
-        let action2 = cx
-            .deserialize_action("test::something::SimpleAction", None)
-            .unwrap();
-        assert_eq!(
-            action1.as_any().downcast_ref::<ComplexAction>().unwrap(),
-            &ComplexAction {
-                arg: "a".to_string(),
-                count: 5,
-            }
-        );
-        assert_eq!(
-            action2.as_any().downcast_ref::<SimpleAction>().unwrap(),
-            &SimpleAction
-        );
-    }
-
-    #[crate::test(self)]
-    fn test_dispatch_action(cx: &mut TestAppContext) {
-        struct ViewA {
-            id: usize,
-            child: Option<AnyViewHandle>,
-        }
-
-        impl Entity for ViewA {
-            type Event = ();
-        }
-
-        impl View for ViewA {
-            fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
-                self.child
-                    .as_ref()
-                    .map(|child| ChildView::new(child, cx).into_any())
-                    .unwrap_or(Empty::new().into_any())
-            }
-
-            fn ui_name() -> &'static str {
-                "View"
-            }
-        }
-
-        struct ViewB {
-            id: usize,
-            child: Option<AnyViewHandle>,
-        }
-
-        impl Entity for ViewB {
-            type Event = ();
-        }
-
-        impl View for ViewB {
-            fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
-                self.child
-                    .as_ref()
-                    .map(|child| ChildView::new(child, cx).into_any())
-                    .unwrap_or(Empty::new().into_any())
-            }
-
-            fn ui_name() -> &'static str {
-                "View"
-            }
-        }
-
-        #[derive(Clone, Default, Deserialize, PartialEq)]
-        pub struct Action(pub String);
-
-        impl_actions!(test, [Action]);
-
-        let actions = Rc::new(RefCell::new(Vec::new()));
-        let observed_actions = Rc::new(RefCell::new(Vec::new()));
-
-        cx.update(|cx| {
-            cx.add_global_action({
-                let actions = actions.clone();
-                move |_: &Action, _: &mut AppContext| {
-                    actions.borrow_mut().push("global".to_string());
-                }
-            });
-
-            cx.add_action({
-                let actions = actions.clone();
-                move |view: &mut ViewA, action: &Action, cx| {
-                    assert_eq!(action.0, "bar");
-                    cx.propagate_action();
-                    actions.borrow_mut().push(format!("{} a", view.id));
-                }
-            });
-
-            cx.add_action({
-                let actions = actions.clone();
-                move |view: &mut ViewA, _: &Action, cx| {
-                    if view.id != 1 {
-                        cx.add_view(|cx| {
-                            cx.propagate_action(); // Still works on a nested ViewContext
-                            ViewB { id: 5, child: None }
-                        });
-                    }
-                    actions.borrow_mut().push(format!("{} b", view.id));
-                }
-            });
-
-            cx.add_action({
-                let actions = actions.clone();
-                move |view: &mut ViewB, _: &Action, cx| {
-                    cx.propagate_action();
-                    actions.borrow_mut().push(format!("{} c", view.id));
-                }
-            });
-
-            cx.add_action({
-                let actions = actions.clone();
-                move |view: &mut ViewB, _: &Action, cx| {
-                    cx.propagate_action();
-                    actions.borrow_mut().push(format!("{} d", view.id));
-                }
-            });
-
-            cx.capture_action({
-                let actions = actions.clone();
-                move |view: &mut ViewA, _: &Action, cx| {
-                    cx.propagate_action();
-                    actions.borrow_mut().push(format!("{} capture", view.id));
-                }
-            });
-
-            cx.observe_actions({
-                let observed_actions = observed_actions.clone();
-                move |action_id, _| observed_actions.borrow_mut().push(action_id)
-            })
-            .detach();
-        });
-
-        let window = cx.add_window(|_| ViewA { id: 1, child: None });
-        let view_1 = window.root(cx);
-        let view_2 = window.update(cx, |cx| {
-            let child = cx.add_view(|_| ViewB { id: 2, child: None });
-            view_1.update(cx, |view, cx| {
-                view.child = Some(child.clone().into_any());
-                cx.notify();
-            });
-            child
-        });
-        let view_3 = window.update(cx, |cx| {
-            let child = cx.add_view(|_| ViewA { id: 3, child: None });
-            view_2.update(cx, |view, cx| {
-                view.child = Some(child.clone().into_any());
-                cx.notify();
-            });
-            child
-        });
-        let view_4 = window.update(cx, |cx| {
-            let child = cx.add_view(|_| ViewB { id: 4, child: None });
-            view_3.update(cx, |view, cx| {
-                view.child = Some(child.clone().into_any());
-                cx.notify();
-            });
-            child
-        });
-
-        window.update(cx, |cx| {
-            cx.dispatch_action(Some(view_4.id()), &Action("bar".to_string()))
-        });
-
-        assert_eq!(
-            *actions.borrow(),
-            vec![
-                "1 capture",
-                "3 capture",
-                "4 d",
-                "4 c",
-                "3 b",
-                "3 a",
-                "2 d",
-                "2 c",
-                "1 b"
-            ]
-        );
-        assert_eq!(*observed_actions.borrow(), [Action::default().id()]);
-
-        // Remove view_1, which doesn't propagate the action
-
-        let window = cx.add_window(|_| ViewB { id: 2, child: None });
-        let view_2 = window.root(cx);
-        let view_3 = window.update(cx, |cx| {
-            let child = cx.add_view(|_| ViewA { id: 3, child: None });
-            view_2.update(cx, |view, cx| {
-                view.child = Some(child.clone().into_any());
-                cx.notify();
-            });
-            child
-        });
-        let view_4 = window.update(cx, |cx| {
-            let child = cx.add_view(|_| ViewB { id: 4, child: None });
-            view_3.update(cx, |view, cx| {
-                view.child = Some(child.clone().into_any());
-                cx.notify();
-            });
-            child
-        });
-
-        actions.borrow_mut().clear();
-        window.update(cx, |cx| {
-            cx.dispatch_action(Some(view_4.id()), &Action("bar".to_string()))
-        });
-
-        assert_eq!(
-            *actions.borrow(),
-            vec![
-                "3 capture",
-                "4 d",
-                "4 c",
-                "3 b",
-                "3 a",
-                "2 d",
-                "2 c",
-                "global"
-            ]
-        );
-        assert_eq!(
-            *observed_actions.borrow(),
-            [Action::default().id(), Action::default().id()]
-        );
-    }
-
-    #[crate::test(self)]
-    fn test_dispatch_keystroke(cx: &mut AppContext) {
-        #[derive(Clone, Deserialize, PartialEq)]
-        pub struct Action(String);
-
-        impl_actions!(test, [Action]);
-
-        struct View {
-            id: usize,
-            keymap_context: KeymapContext,
-            child: Option<AnyViewHandle>,
-        }
-
-        impl Entity for View {
-            type Event = ();
-        }
-
-        impl super::View for View {
-            fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
-                self.child
-                    .as_ref()
-                    .map(|child| ChildView::new(child, cx).into_any())
-                    .unwrap_or(Empty::new().into_any())
-            }
-
-            fn ui_name() -> &'static str {
-                "View"
-            }
-
-            fn update_keymap_context(&self, keymap: &mut KeymapContext, _: &AppContext) {
-                *keymap = self.keymap_context.clone();
-            }
-        }
-
-        impl View {
-            fn new(id: usize) -> Self {
-                View {
-                    id,
-                    keymap_context: KeymapContext::default(),
-                    child: None,
-                }
-            }
-        }
-
-        let mut view_1 = View::new(1);
-        let mut view_2 = View::new(2);
-        let mut view_3 = View::new(3);
-        view_1.keymap_context.add_identifier("a");
-        view_2.keymap_context.add_identifier("a");
-        view_2.keymap_context.add_identifier("b");
-        view_3.keymap_context.add_identifier("a");
-        view_3.keymap_context.add_identifier("b");
-        view_3.keymap_context.add_identifier("c");
-
-        let window = cx.add_window(Default::default(), |cx| {
-            let view_2 = cx.add_view(|cx| {
-                let view_3 = cx.add_view(|cx| {
-                    cx.focus_self();
-                    view_3
-                });
-                view_2.child = Some(view_3.into_any());
-                view_2
-            });
-            view_1.child = Some(view_2.into_any());
-            view_1
-        });
-
-        // This binding only dispatches an action on view 2 because that view will have
-        // "a" and "b" in its context, but not "c".
-        cx.add_bindings(vec![Binding::new(
-            "a",
-            Action("a".to_string()),
-            Some("a && b && !c"),
-        )]);
-
-        cx.add_bindings(vec![Binding::new("b", Action("b".to_string()), None)]);
-
-        // This binding only dispatches an action on views 2 and 3, because they have
-        // a parent view with a in its context
-        cx.add_bindings(vec![Binding::new(
-            "c",
-            Action("c".to_string()),
-            Some("b > c"),
-        )]);
-
-        // This binding only dispatches an action on view 2, because they have
-        // a parent view with a in its context
-        cx.add_bindings(vec![Binding::new(
-            "d",
-            Action("d".to_string()),
-            Some("a && !b > b"),
-        )]);
-
-        let actions = Rc::new(RefCell::new(Vec::new()));
-        cx.add_action({
-            let actions = actions.clone();
-            move |view: &mut View, action: &Action, cx| {
-                actions
-                    .borrow_mut()
-                    .push(format!("{} {}", view.id, action.0));
-
-                if action.0 == "b" {
-                    cx.propagate_action();
-                }
-            }
-        });
-
-        cx.add_global_action({
-            let actions = actions.clone();
-            move |action: &Action, _| {
-                actions.borrow_mut().push(format!("global {}", action.0));
-            }
-        });
-
-        window.update(cx, |cx| {
-            cx.dispatch_keystroke(&Keystroke::parse("a").unwrap())
-        });
-        assert_eq!(&*actions.borrow(), &["2 a"]);
-        actions.borrow_mut().clear();
-
-        window.update(cx, |cx| {
-            cx.dispatch_keystroke(&Keystroke::parse("b").unwrap());
-        });
-
-        assert_eq!(&*actions.borrow(), &["3 b", "2 b", "1 b", "global b"]);
-        actions.borrow_mut().clear();
-
-        window.update(cx, |cx| {
-            cx.dispatch_keystroke(&Keystroke::parse("c").unwrap());
-        });
-        assert_eq!(&*actions.borrow(), &["3 c"]);
-        actions.borrow_mut().clear();
-
-        window.update(cx, |cx| {
-            cx.dispatch_keystroke(&Keystroke::parse("d").unwrap());
-        });
-        assert_eq!(&*actions.borrow(), &["2 d"]);
-        actions.borrow_mut().clear();
-    }
-
-    #[crate::test(self)]
-    fn test_keystrokes_for_action(cx: &mut TestAppContext) {
-        actions!(test, [Action1, Action2, Action3, GlobalAction]);
-
-        struct View1 {
-            child: ViewHandle<View2>,
-        }
-        struct View2 {}
-
-        impl Entity for View1 {
-            type Event = ();
-        }
-        impl Entity for View2 {
-            type Event = ();
-        }
-
-        impl super::View for View1 {
-            fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
-                ChildView::new(&self.child, cx).into_any()
-            }
-            fn ui_name() -> &'static str {
-                "View1"
-            }
-        }
-        impl super::View for View2 {
-            fn render(&mut self, _: &mut ViewContext<Self>) -> AnyElement<Self> {
-                Empty::new().into_any()
-            }
-            fn ui_name() -> &'static str {
-                "View2"
-            }
-        }
-
-        let window = cx.add_window(|cx| {
-            let view_2 = cx.add_view(|cx| {
-                cx.focus_self();
-                View2 {}
-            });
-            View1 { child: view_2 }
-        });
-        let view_1 = window.root(cx);
-        let view_2 = view_1.read_with(cx, |view, _| view.child.clone());
-
-        cx.update(|cx| {
-            cx.add_action(|_: &mut View1, _: &Action1, _cx| {});
-            cx.add_action(|_: &mut View1, _: &Action3, _cx| {});
-            cx.add_action(|_: &mut View2, _: &Action2, _cx| {});
-            cx.add_global_action(|_: &GlobalAction, _| {});
-            cx.add_bindings(vec![
-                Binding::new("a", Action1, Some("View1")),
-                Binding::new("b", Action2, Some("View1 > View2")),
-                Binding::new("c", Action3, Some("View2")),
-                Binding::new("d", GlobalAction, Some("View3")), // View 3 does not exist
-            ]);
-        });
-
-        let view_1_id = view_1.id();
-        view_1.update(cx, |_, cx| {
-            view_2.update(cx, |_, cx| {
-                // Sanity check
-                assert_eq!(
-                    cx.keystrokes_for_action(view_1_id, &Action1)
-                        .unwrap()
-                        .as_slice(),
-                    &[Keystroke::parse("a").unwrap()]
-                );
-                assert_eq!(
-                    cx.keystrokes_for_action(view_2.id(), &Action2)
-                        .unwrap()
-                        .as_slice(),
-                    &[Keystroke::parse("b").unwrap()]
-                );
-                assert_eq!(cx.keystrokes_for_action(view_1.id(), &Action3), None);
-                assert_eq!(
-                    cx.keystrokes_for_action(view_2.id(), &Action3)
-                        .unwrap()
-                        .as_slice(),
-                    &[Keystroke::parse("c").unwrap()]
-                );
-
-                // The 'a' keystroke propagates up the view tree from view_2
-                // to view_1. The action, Action1, is handled by view_1.
-                assert_eq!(
-                    cx.keystrokes_for_action(view_2.id(), &Action1)
-                        .unwrap()
-                        .as_slice(),
-                    &[Keystroke::parse("a").unwrap()]
-                );
-
-                // Actions that are handled below the current view don't have bindings
-                assert_eq!(cx.keystrokes_for_action(view_1_id, &Action2), None);
-
-                // Actions that are handled in other branches of the tree should not have a binding
-                assert_eq!(cx.keystrokes_for_action(view_2.id(), &GlobalAction), None);
-            });
-        });
-
-        // Check that global actions do not have a binding, even if a binding does exist in another view
-        assert_eq!(
-            &available_actions(window.into(), view_1.id(), cx),
-            &[
-                ("test::Action1", vec![Keystroke::parse("a").unwrap()]),
-                ("test::Action3", vec![]),
-                ("test::GlobalAction", vec![]),
-            ],
-        );
-
-        // Check that view 1 actions and bindings are available even when called from view 2
-        assert_eq!(
-            &available_actions(window.into(), view_2.id(), cx),
-            &[
-                ("test::Action1", vec![Keystroke::parse("a").unwrap()]),
-                ("test::Action2", vec![Keystroke::parse("b").unwrap()]),
-                ("test::Action3", vec![Keystroke::parse("c").unwrap()]),
-                ("test::GlobalAction", vec![]),
-            ],
-        );
-
-        // Produces a list of actions and key bindings
-        fn available_actions(
-            window: AnyWindowHandle,
-            view_id: usize,
-            cx: &TestAppContext,
-        ) -> Vec<(&'static str, Vec<Keystroke>)> {
-            cx.available_actions(window.into(), view_id)
-                .into_iter()
-                .map(|(action_name, _, bindings)| {
-                    (
-                        action_name,
-                        bindings
-                            .iter()
-                            .map(|binding| binding.keystrokes()[0].clone())
-                            .collect::<Vec<_>>(),
-                    )
-                })
-                .sorted_by(|(name1, _), (name2, _)| name1.cmp(name2))
-                .collect()
-        }
-    }
-
-    #[crate::test(self)]
-    fn test_keystrokes_for_action_with_data(cx: &mut TestAppContext) {
-        #[derive(Clone, Debug, Deserialize, PartialEq)]
-        struct ActionWithArg {
-            #[serde(default)]
-            arg: bool,
-        }
-
-        struct View;
-        impl super::Entity for View {
-            type Event = ();
-        }
-        impl super::View for View {
-            fn render(&mut self, _: &mut ViewContext<Self>) -> AnyElement<Self> {
-                Empty::new().into_any()
-            }
-            fn ui_name() -> &'static str {
-                "View"
-            }
-        }
-
-        impl_actions!(test, [ActionWithArg]);
-
-        let window = cx.add_window(|_| View);
-        let view = window.root(cx);
-        cx.update(|cx| {
-            cx.add_global_action(|_: &ActionWithArg, _| {});
-            cx.add_bindings(vec![
-                Binding::new("a", ActionWithArg { arg: false }, None),
-                Binding::new("shift-a", ActionWithArg { arg: true }, None),
-            ]);
-        });
-
-        let actions = cx.available_actions(window.into(), view.id());
-        assert_eq!(
-            actions[0].1.as_any().downcast_ref::<ActionWithArg>(),
-            Some(&ActionWithArg { arg: false })
-        );
-        assert_eq!(
-            actions[0]
-                .2
-                .iter()
-                .map(|b| b.keystrokes()[0].clone())
-                .collect::<Vec<_>>(),
-            vec![Keystroke::parse("a").unwrap()],
-        );
-    }
-
-    #[crate::test(self)]
-    async fn test_model_condition(cx: &mut TestAppContext) {
-        struct Counter(usize);
-
-        impl super::Entity for Counter {
-            type Event = ();
-        }
-
-        impl Counter {
-            fn inc(&mut self, cx: &mut ModelContext<Self>) {
-                self.0 += 1;
-                cx.notify();
-            }
-        }
-
-        let model = cx.add_model(|_| Counter(0));
-
-        let condition1 = model.condition(cx, |model, _| model.0 == 2);
-        let condition2 = model.condition(cx, |model, _| model.0 == 3);
-        smol::pin!(condition1, condition2);
-
-        model.update(cx, |model, cx| model.inc(cx));
-        assert_eq!(poll_once(&mut condition1).await, None);
-        assert_eq!(poll_once(&mut condition2).await, None);
-
-        model.update(cx, |model, cx| model.inc(cx));
-        assert_eq!(poll_once(&mut condition1).await, Some(()));
-        assert_eq!(poll_once(&mut condition2).await, None);
-
-        model.update(cx, |model, cx| model.inc(cx));
-        assert_eq!(poll_once(&mut condition2).await, Some(()));
-
-        model.update(cx, |_, cx| cx.notify());
-    }
-
-    #[crate::test(self)]
-    #[should_panic]
-    async fn test_model_condition_timeout(cx: &mut TestAppContext) {
-        struct Model;
-
-        impl super::Entity for Model {
-            type Event = ();
-        }
-
-        let model = cx.add_model(|_| Model);
-        model.condition(cx, |_, _| false).await;
-    }
-
-    #[crate::test(self)]
-    #[should_panic(expected = "model dropped with pending condition")]
-    async fn test_model_condition_panic_on_drop(cx: &mut TestAppContext) {
-        struct Model;
-
-        impl super::Entity for Model {
-            type Event = ();
-        }
-
-        let model = cx.add_model(|_| Model);
-        let condition = model.condition(cx, |_, _| false);
-        cx.update(|_| drop(model));
-        condition.await;
-    }
-
-    #[crate::test(self)]
-    async fn test_view_condition(cx: &mut TestAppContext) {
-        struct Counter(usize);
-
-        impl super::Entity for Counter {
-            type Event = ();
-        }
-
-        impl super::View for Counter {
-            fn ui_name() -> &'static str {
-                "test view"
-            }
-
-            fn render(&mut self, _: &mut ViewContext<Self>) -> AnyElement<Self> {
-                Empty::new().into_any()
-            }
-        }
-
-        impl Counter {
-            fn inc(&mut self, cx: &mut ViewContext<Self>) {
-                self.0 += 1;
-                cx.notify();
-            }
-        }
-
-        let window = cx.add_window(|_| Counter(0));
-        let view = window.root(cx);
-
-        let condition1 = view.condition(cx, |view, _| view.0 == 2);
-        let condition2 = view.condition(cx, |view, _| view.0 == 3);
-        smol::pin!(condition1, condition2);
-
-        view.update(cx, |view, cx| view.inc(cx));
-        assert_eq!(poll_once(&mut condition1).await, None);
-        assert_eq!(poll_once(&mut condition2).await, None);
-
-        view.update(cx, |view, cx| view.inc(cx));
-        assert_eq!(poll_once(&mut condition1).await, Some(()));
-        assert_eq!(poll_once(&mut condition2).await, None);
-
-        view.update(cx, |view, cx| view.inc(cx));
-        assert_eq!(poll_once(&mut condition2).await, Some(()));
-        view.update(cx, |_, cx| cx.notify());
-    }
-
-    #[crate::test(self)]
-    #[should_panic]
-    async fn test_view_condition_timeout(cx: &mut TestAppContext) {
-        let window = cx.add_window(|_| TestView::default());
-        window.root(cx).condition(cx, |_, _| false).await;
-    }
-
-    #[crate::test(self)]
-    #[should_panic(expected = "view dropped with pending condition")]
-    async fn test_view_condition_panic_on_drop(cx: &mut TestAppContext) {
-        let window = cx.add_window(|_| TestView::default());
-        let view = window.add_view(cx, |_| TestView::default());
-
-        let condition = view.condition(cx, |_, _| false);
-        cx.update(|_| drop(view));
-        condition.await;
-    }
-
-    #[crate::test(self)]
-    fn test_refresh_windows(cx: &mut TestAppContext) {
-        struct View(usize);
-
-        impl super::Entity for View {
-            type Event = ();
-        }
-
-        impl super::View for View {
-            fn ui_name() -> &'static str {
-                "test view"
-            }
-
-            fn render(&mut self, _: &mut ViewContext<Self>) -> AnyElement<Self> {
-                Empty::new().into_any_named(format!("render count: {}", post_inc(&mut self.0)))
-            }
-        }
-
-        let window = cx.add_window(|_| View(0));
-        let root_view = window.root(cx);
-        window.update(cx, |cx| {
-            assert_eq!(
-                cx.window.rendered_views[&root_view.id()].name(),
-                Some("render count: 0")
-            );
-        });
-
-        let view = window.update(cx, |cx| {
-            cx.refresh_windows();
-            cx.add_view(|_| View(0))
-        });
-
-        window.update(cx, |cx| {
-            assert_eq!(
-                cx.window.rendered_views[&root_view.id()].name(),
-                Some("render count: 1")
-            );
-            assert_eq!(
-                cx.window.rendered_views[&view.id()].name(),
-                Some("render count: 0")
-            );
-        });
-
-        cx.update(|cx| cx.refresh_windows());
-
-        window.update(cx, |cx| {
-            assert_eq!(
-                cx.window.rendered_views[&root_view.id()].name(),
-                Some("render count: 2")
-            );
-            assert_eq!(
-                cx.window.rendered_views[&view.id()].name(),
-                Some("render count: 1")
-            );
-        });
-
-        cx.update(|cx| {
-            cx.refresh_windows();
-            drop(view);
-        });
-
-        window.update(cx, |cx| {
-            assert_eq!(
-                cx.window.rendered_views[&root_view.id()].name(),
-                Some("render count: 3")
-            );
-            assert_eq!(cx.window.rendered_views.len(), 1);
-        });
-    }
-
-    #[crate::test(self)]
-    async fn test_labeled_tasks(cx: &mut TestAppContext) {
-        assert_eq!(None, cx.update(|cx| cx.active_labeled_tasks().next()));
-        let (mut sender, mut receiver) = postage::oneshot::channel::<()>();
-        let task = cx
-            .update(|cx| cx.spawn_labeled("Test Label", |_| async move { receiver.recv().await }));
-
-        assert_eq!(
-            Some("Test Label"),
-            cx.update(|cx| cx.active_labeled_tasks().next())
-        );
-        sender
-            .send(())
-            .await
-            .expect("Could not send message to complete task");
-        task.await;
-
-        assert_eq!(None, cx.update(|cx| cx.active_labeled_tasks().next()));
-    }
-
-    #[crate::test(self)]
-    async fn test_window_activation(cx: &mut TestAppContext) {
-        struct View(&'static str);
-
-        impl super::Entity for View {
-            type Event = ();
-        }
-
-        impl super::View for View {
-            fn ui_name() -> &'static str {
-                "test view"
-            }
-
-            fn render(&mut self, _: &mut ViewContext<Self>) -> AnyElement<Self> {
-                Empty::new().into_any()
-            }
-        }
-
-        let events = Rc::new(RefCell::new(Vec::new()));
-        let window_1 = cx.add_window(|cx: &mut ViewContext<View>| {
-            cx.observe_window_activation({
-                let events = events.clone();
-                move |this, active, _| events.borrow_mut().push((this.0, active))
-            })
-            .detach();
-            View("window 1")
-        });
-        assert_eq!(mem::take(&mut *events.borrow_mut()), [("window 1", true)]);
-
-        let window_2 = cx.add_window(|cx: &mut ViewContext<View>| {
-            cx.observe_window_activation({
-                let events = events.clone();
-                move |this, active, _| events.borrow_mut().push((this.0, active))
-            })
-            .detach();
-            View("window 2")
-        });
-        assert_eq!(
-            mem::take(&mut *events.borrow_mut()),
-            [("window 1", false), ("window 2", true)]
-        );
-
-        let window_3 = cx.add_window(|cx: &mut ViewContext<View>| {
-            cx.observe_window_activation({
-                let events = events.clone();
-                move |this, active, _| events.borrow_mut().push((this.0, active))
-            })
-            .detach();
-            View("window 3")
-        });
-        assert_eq!(
-            mem::take(&mut *events.borrow_mut()),
-            [("window 2", false), ("window 3", true)]
-        );
-
-        window_2.simulate_activation(cx);
-        assert_eq!(
-            mem::take(&mut *events.borrow_mut()),
-            [("window 3", false), ("window 2", true)]
-        );
-
-        window_1.simulate_activation(cx);
-        assert_eq!(
-            mem::take(&mut *events.borrow_mut()),
-            [("window 2", false), ("window 1", true)]
-        );
-
-        window_3.simulate_activation(cx);
-        assert_eq!(
-            mem::take(&mut *events.borrow_mut()),
-            [("window 1", false), ("window 3", true)]
-        );
-
-        window_3.simulate_activation(cx);
-        assert_eq!(mem::take(&mut *events.borrow_mut()), []);
-    }
-
-    #[crate::test(self)]
-    fn test_child_view(cx: &mut TestAppContext) {
-        struct Child {
-            rendered: Rc<Cell<bool>>,
-            dropped: Rc<Cell<bool>>,
-        }
-
-        impl super::Entity for Child {
-            type Event = ();
-        }
-
-        impl super::View for Child {
-            fn ui_name() -> &'static str {
-                "child view"
-            }
-
-            fn render(&mut self, _: &mut ViewContext<Self>) -> AnyElement<Self> {
-                self.rendered.set(true);
-                Empty::new().into_any()
-            }
-        }
-
-        impl Drop for Child {
-            fn drop(&mut self) {
-                self.dropped.set(true);
-            }
-        }
-
-        struct Parent {
-            child: Option<ViewHandle<Child>>,
-        }
-
-        impl super::Entity for Parent {
-            type Event = ();
-        }
-
-        impl super::View for Parent {
-            fn ui_name() -> &'static str {
-                "parent view"
-            }
-
-            fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
-                if let Some(child) = self.child.as_ref() {
-                    ChildView::new(child, cx).into_any()
-                } else {
-                    Empty::new().into_any()
-                }
-            }
-        }
-
-        let child_rendered = Rc::new(Cell::new(false));
-        let child_dropped = Rc::new(Cell::new(false));
-        let window = cx.add_window(|cx| Parent {
-            child: Some(cx.add_view(|_| Child {
-                rendered: child_rendered.clone(),
-                dropped: child_dropped.clone(),
-            })),
-        });
-        let root_view = window.root(cx);
-        assert!(child_rendered.take());
-        assert!(!child_dropped.take());
-
-        root_view.update(cx, |view, cx| {
-            view.child.take();
-            cx.notify();
-        });
-        assert!(!child_rendered.take());
-        assert!(child_dropped.take());
-    }
-
-    #[derive(Default)]
-    struct TestView {
-        events: Vec<String>,
-    }
-
-    impl Entity for TestView {
-        type Event = String;
-    }
-
-    impl View for TestView {
-        fn ui_name() -> &'static str {
-            "TestView"
-        }
-
-        fn render(&mut self, _: &mut ViewContext<Self>) -> AnyElement<Self> {
-            Empty::new().into_any()
-        }
-    }
+#[derive(Debug)]
+pub struct KeystrokeEvent {
+    pub keystroke: Keystroke,
+    pub action: Option<Box<dyn Action>>,
 }

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

@@ -1,120 +0,0 @@
-use std::any::{Any, TypeId};
-
-use crate::TypeTag;
-
-pub trait Action: 'static {
-    fn id(&self) -> TypeId;
-    fn namespace(&self) -> &'static str;
-    fn name(&self) -> &'static str;
-    fn as_any(&self) -> &dyn Any;
-    fn type_tag(&self) -> TypeTag;
-    fn boxed_clone(&self) -> Box<dyn Action>;
-    fn eq(&self, other: &dyn Action) -> bool;
-
-    fn qualified_name() -> &'static str
-    where
-        Self: Sized;
-    fn from_json_str(json: serde_json::Value) -> anyhow::Result<Box<dyn Action>>
-    where
-        Self: Sized;
-}
-
-impl std::fmt::Debug for dyn Action {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("dyn Action")
-            .field("namespace", &self.namespace())
-            .field("name", &self.name())
-            .finish()
-    }
-}
-/// Define a set of unit struct types that all implement the `Action` trait.
-///
-/// The first argument is a namespace that will be associated with each of
-/// the given action types, to ensure that they have globally unique
-/// qualified names for use in keymap files.
-#[macro_export]
-macro_rules! actions {
-    ($namespace:path, [ $($name:ident),* $(,)? ]) => {
-        $(
-            #[derive(Clone, Debug, Default, PartialEq, Eq)]
-            pub struct $name;
-            $crate::__impl_action! {
-                $namespace,
-                $name,
-                fn from_json_str(_: $crate::serde_json::Value) -> $crate::anyhow::Result<Box<dyn $crate::Action>> {
-                    Ok(Box::new(Self))
-                }
-            }
-        )*
-    };
-}
-
-/// Implement the `Action` trait for a set of existing types.
-///
-/// The first argument is a namespace that will be associated with each of
-/// the given action types, to ensure that they have globally unique
-/// qualified names for use in keymap files.
-#[macro_export]
-macro_rules! impl_actions {
-    ($namespace:path, [ $($name:ident),* $(,)? ]) => {
-        $(
-            $crate::__impl_action! {
-                $namespace,
-                $name,
-                fn from_json_str(json: $crate::serde_json::Value) -> $crate::anyhow::Result<Box<dyn $crate::Action>> {
-                    Ok(Box::new($crate::serde_json::from_value::<Self>(json)?))
-                }
-            }
-        )*
-    };
-}
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! __impl_action {
-    ($namespace:path, $name:ident, $from_json_fn:item) => {
-        impl $crate::action::Action for $name {
-            fn namespace(&self) -> &'static str {
-                stringify!($namespace)
-            }
-
-            fn name(&self) -> &'static str {
-                stringify!($name)
-            }
-
-            fn qualified_name() -> &'static str {
-                concat!(
-                    stringify!($namespace),
-                    "::",
-                    stringify!($name),
-                )
-            }
-
-            fn id(&self) -> std::any::TypeId {
-                std::any::TypeId::of::<$name>()
-            }
-
-            fn as_any(&self) -> &dyn std::any::Any {
-                self
-            }
-
-            fn boxed_clone(&self) -> Box<dyn $crate::Action> {
-                Box::new(self.clone())
-            }
-
-            fn eq(&self, other: &dyn $crate::Action) -> bool {
-                if let Some(other) = other.as_any().downcast_ref::<Self>() {
-                    self == other
-                } else {
-                    false
-                }
-            }
-
-            fn type_tag(&self) -> $crate::TypeTag {
-                $crate::TypeTag::new::<Self>()
-            }
-
-            $from_json_fn
-        }
-    };
-}

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

@@ -1,164 +0,0 @@
-use collections::{BTreeMap, HashMap, HashSet};
-use parking_lot::Mutex;
-use std::sync::Arc;
-use std::{hash::Hash, sync::Weak};
-
-pub struct CallbackCollection<K: Clone + Hash + Eq, F> {
-    internal: Arc<Mutex<Mapping<K, F>>>,
-}
-
-pub struct Subscription<K: Clone + Hash + Eq, F> {
-    key: K,
-    id: usize,
-    mapping: Option<Weak<Mutex<Mapping<K, F>>>>,
-}
-
-struct Mapping<K, F> {
-    callbacks: HashMap<K, BTreeMap<usize, F>>,
-    dropped_subscriptions: HashMap<K, HashSet<usize>>,
-}
-
-impl<K: Hash + Eq, F> Mapping<K, F> {
-    fn clear_dropped_state(&mut self, key: &K, subscription_id: usize) -> bool {
-        if let Some(subscriptions) = self.dropped_subscriptions.get_mut(&key) {
-            subscriptions.remove(&subscription_id)
-        } else {
-            false
-        }
-    }
-}
-
-impl<K, F> Default for Mapping<K, F> {
-    fn default() -> Self {
-        Self {
-            callbacks: Default::default(),
-            dropped_subscriptions: Default::default(),
-        }
-    }
-}
-
-impl<K: Clone + Hash + Eq, F> Clone for CallbackCollection<K, F> {
-    fn clone(&self) -> Self {
-        Self {
-            internal: self.internal.clone(),
-        }
-    }
-}
-
-impl<K: Clone + Hash + Eq + Copy, F> Default for CallbackCollection<K, F> {
-    fn default() -> Self {
-        CallbackCollection {
-            internal: Arc::new(Mutex::new(Default::default())),
-        }
-    }
-}
-
-impl<K: Clone + Hash + Eq + Copy, F> CallbackCollection<K, F> {
-    #[cfg(test)]
-    pub fn is_empty(&self) -> bool {
-        self.internal.lock().callbacks.is_empty()
-    }
-
-    pub fn subscribe(&mut self, key: K, subscription_id: usize) -> Subscription<K, F> {
-        Subscription {
-            key,
-            id: subscription_id,
-            mapping: Some(Arc::downgrade(&self.internal)),
-        }
-    }
-
-    pub fn add_callback(&mut self, key: K, subscription_id: usize, callback: F) {
-        let mut this = self.internal.lock();
-
-        // If this callback's subscription was dropped before the callback was
-        // added, then just drop the callback.
-        if this.clear_dropped_state(&key, subscription_id) {
-            return;
-        }
-
-        this.callbacks
-            .entry(key)
-            .or_default()
-            .insert(subscription_id, callback);
-    }
-
-    pub fn remove(&mut self, key: K) {
-        // Drop these callbacks after releasing the lock, in case one of them
-        // owns a subscription to this callback collection.
-        let mut this = self.internal.lock();
-        let callbacks = this.callbacks.remove(&key);
-        this.dropped_subscriptions.remove(&key);
-        drop(this);
-        drop(callbacks);
-    }
-
-    pub fn emit<C>(&mut self, key: K, mut call_callback: C)
-    where
-        C: FnMut(&mut F) -> bool,
-    {
-        let callbacks = self.internal.lock().callbacks.remove(&key);
-        if let Some(callbacks) = callbacks {
-            for (subscription_id, mut callback) in callbacks {
-                // If this callback's subscription was dropped while invoking an
-                // earlier callback, then just drop the callback.
-                let mut this = self.internal.lock();
-                if this.clear_dropped_state(&key, subscription_id) {
-                    continue;
-                }
-
-                drop(this);
-                let alive = call_callback(&mut callback);
-
-                // If this callback's subscription was dropped while invoking the callback
-                // itself, or if the callback returns false, then just drop the callback.
-                let mut this = self.internal.lock();
-                if this.clear_dropped_state(&key, subscription_id) || !alive {
-                    continue;
-                }
-
-                this.callbacks
-                    .entry(key)
-                    .or_default()
-                    .insert(subscription_id, callback);
-            }
-        }
-    }
-}
-
-impl<K: Clone + Hash + Eq, F> Subscription<K, F> {
-    pub fn id(&self) -> usize {
-        self.id
-    }
-
-    pub fn detach(&mut self) {
-        self.mapping.take();
-    }
-}
-
-impl<K: Clone + Hash + Eq, F> Drop for Subscription<K, F> {
-    fn drop(&mut self) {
-        if let Some(mapping) = self.mapping.as_ref().and_then(|mapping| mapping.upgrade()) {
-            let mut mapping = mapping.lock();
-
-            // If the callback is present in the mapping, then just remove it.
-            if let Some(callbacks) = mapping.callbacks.get_mut(&self.key) {
-                let callback = callbacks.remove(&self.id);
-                if callback.is_some() {
-                    drop(mapping);
-                    drop(callback);
-                    return;
-                }
-            }
-
-            // If this subscription's callback is not present, then either it has been
-            // temporarily removed during emit, or it has not yet been added. Record
-            // that this subscription has been dropped so that the callback can be
-            // removed later.
-            mapping
-                .dropped_subscriptions
-                .entry(self.key.clone())
-                .or_default()
-                .insert(self.id);
-        }
-    }
-}

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

@@ -1,99 +0,0 @@
-use crate::{platform::ForegroundPlatform, Action, App, AppContext};
-
-pub struct Menu<'a> {
-    pub name: &'a str,
-    pub items: Vec<MenuItem<'a>>,
-}
-
-pub enum MenuItem<'a> {
-    Separator,
-    Submenu(Menu<'a>),
-    Action {
-        name: &'a str,
-        action: Box<dyn Action>,
-        os_action: Option<OsAction>,
-    },
-}
-
-impl<'a> MenuItem<'a> {
-    pub fn separator() -> Self {
-        Self::Separator
-    }
-
-    pub fn submenu(menu: Menu<'a>) -> Self {
-        Self::Submenu(menu)
-    }
-
-    pub fn action(name: &'a str, action: impl Action) -> Self {
-        Self::Action {
-            name,
-            action: Box::new(action),
-            os_action: None,
-        }
-    }
-
-    pub fn os_action(name: &'a str, action: impl Action, os_action: OsAction) -> Self {
-        Self::Action {
-            name,
-            action: Box::new(action),
-            os_action: Some(os_action),
-        }
-    }
-}
-
-#[derive(Copy, Clone, Eq, PartialEq)]
-pub enum OsAction {
-    Cut,
-    Copy,
-    Paste,
-    SelectAll,
-    Undo,
-    Redo,
-}
-
-impl AppContext {
-    pub fn set_menus(&mut self, menus: Vec<Menu>) {
-        self.foreground_platform
-            .set_menus(menus, &self.keystroke_matcher);
-    }
-}
-
-pub(crate) fn setup_menu_handlers(foreground_platform: &dyn ForegroundPlatform, app: &App) {
-    foreground_platform.on_will_open_menu(Box::new({
-        let cx = app.0.clone();
-        move || {
-            let mut cx = cx.borrow_mut();
-            cx.keystroke_matcher.clear_pending();
-        }
-    }));
-    foreground_platform.on_validate_menu_command(Box::new({
-        let cx = app.0.clone();
-        move |action| {
-            let cx = cx.borrow_mut();
-            !cx.keystroke_matcher.has_pending_keystrokes() && cx.is_action_available(action)
-        }
-    }));
-    foreground_platform.on_menu_command(Box::new({
-        let cx = app.0.clone();
-        move |action| {
-            let mut cx = cx.borrow_mut();
-            if let Some(main_window) = cx.active_window() {
-                let dispatched = main_window
-                    .update(&mut *cx, |cx| {
-                        if let Some(view_id) = cx.focused_view_id() {
-                            cx.dispatch_action(Some(view_id), action);
-                            true
-                        } else {
-                            false
-                        }
-                    })
-                    .unwrap_or(false);
-
-                if dispatched {
-                    return;
-                }
-            }
-            cx.dispatch_global_action_any(action);
-        }
-    }));
-}

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

@@ -1,220 +0,0 @@
-#[cfg(any(test, feature = "test-support"))]
-use std::sync::Arc;
-
-use lazy_static::lazy_static;
-#[cfg(any(test, feature = "test-support"))]
-use parking_lot::Mutex;
-
-use collections::{hash_map::Entry, HashMap, HashSet};
-
-#[cfg(any(test, feature = "test-support"))]
-use crate::util::post_inc;
-use crate::{AnyWindowHandle, ElementStateId};
-
-lazy_static! {
-    static ref LEAK_BACKTRACE: bool =
-        std::env::var("LEAK_BACKTRACE").map_or(false, |b| !b.is_empty());
-}
-
-struct ElementStateRefCount {
-    ref_count: usize,
-    frame_id: usize,
-}
-
-#[derive(Default)]
-pub struct RefCounts {
-    entity_counts: HashMap<usize, usize>,
-    element_state_counts: HashMap<ElementStateId, ElementStateRefCount>,
-    dropped_models: HashSet<usize>,
-    dropped_views: HashSet<(AnyWindowHandle, usize)>,
-    dropped_element_states: HashSet<ElementStateId>,
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub leak_detector: Arc<Mutex<LeakDetector>>,
-}
-
-impl RefCounts {
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn new(leak_detector: Arc<Mutex<LeakDetector>>) -> Self {
-        Self {
-            #[cfg(any(test, feature = "test-support"))]
-            leak_detector,
-            ..Default::default()
-        }
-    }
-
-    pub fn inc_model(&mut self, model_id: usize) {
-        match self.entity_counts.entry(model_id) {
-            Entry::Occupied(mut entry) => {
-                *entry.get_mut() += 1;
-            }
-            Entry::Vacant(entry) => {
-                entry.insert(1);
-                self.dropped_models.remove(&model_id);
-            }
-        }
-    }
-
-    pub fn inc_view(&mut self, window: AnyWindowHandle, view_id: usize) {
-        match self.entity_counts.entry(view_id) {
-            Entry::Occupied(mut entry) => *entry.get_mut() += 1,
-            Entry::Vacant(entry) => {
-                entry.insert(1);
-                self.dropped_views.remove(&(window, view_id));
-            }
-        }
-    }
-
-    pub fn inc_element_state(&mut self, id: ElementStateId, frame_id: usize) {
-        match self.element_state_counts.entry(id) {
-            Entry::Occupied(mut entry) => {
-                let entry = entry.get_mut();
-                if entry.frame_id == frame_id || entry.ref_count >= 2 {
-                    panic!("used the same element state more than once in the same frame");
-                }
-                entry.ref_count += 1;
-                entry.frame_id = frame_id;
-            }
-            Entry::Vacant(entry) => {
-                entry.insert(ElementStateRefCount {
-                    ref_count: 1,
-                    frame_id,
-                });
-                self.dropped_element_states.remove(&id);
-            }
-        }
-    }
-
-    pub fn dec_model(&mut self, model_id: usize) {
-        let count = self.entity_counts.get_mut(&model_id).unwrap();
-        *count -= 1;
-        if *count == 0 {
-            self.entity_counts.remove(&model_id);
-            self.dropped_models.insert(model_id);
-        }
-    }
-
-    pub fn dec_view(&mut self, window: AnyWindowHandle, view_id: usize) {
-        let count = self.entity_counts.get_mut(&view_id).unwrap();
-        *count -= 1;
-        if *count == 0 {
-            self.entity_counts.remove(&view_id);
-            self.dropped_views.insert((window, view_id));
-        }
-    }
-
-    pub fn dec_element_state(&mut self, id: ElementStateId) {
-        let entry = self.element_state_counts.get_mut(&id).unwrap();
-        entry.ref_count -= 1;
-        if entry.ref_count == 0 {
-            self.element_state_counts.remove(&id);
-            self.dropped_element_states.insert(id);
-        }
-    }
-
-    pub fn is_entity_alive(&self, entity_id: usize) -> bool {
-        self.entity_counts.contains_key(&entity_id)
-    }
-
-    pub fn take_dropped(
-        &mut self,
-    ) -> (
-        HashSet<usize>,
-        HashSet<(AnyWindowHandle, usize)>,
-        HashSet<ElementStateId>,
-    ) {
-        (
-            std::mem::take(&mut self.dropped_models),
-            std::mem::take(&mut self.dropped_views),
-            std::mem::take(&mut self.dropped_element_states),
-        )
-    }
-}
-
-#[cfg(any(test, feature = "test-support"))]
-#[derive(Default)]
-pub struct LeakDetector {
-    next_handle_id: usize,
-    #[allow(clippy::type_complexity)]
-    handle_backtraces: HashMap<
-        usize,
-        (
-            Option<&'static str>,
-            HashMap<usize, Option<backtrace::Backtrace>>,
-        ),
-    >,
-}
-
-#[cfg(any(test, feature = "test-support"))]
-impl LeakDetector {
-    pub fn handle_created(&mut self, type_name: Option<&'static str>, entity_id: usize) -> usize {
-        let handle_id = post_inc(&mut self.next_handle_id);
-        let entry = self.handle_backtraces.entry(entity_id).or_default();
-        let backtrace = if *LEAK_BACKTRACE {
-            Some(backtrace::Backtrace::new_unresolved())
-        } else {
-            None
-        };
-        if let Some(type_name) = type_name {
-            entry.0.get_or_insert(type_name);
-        }
-        entry.1.insert(handle_id, backtrace);
-        handle_id
-    }
-
-    pub fn handle_dropped(&mut self, entity_id: usize, handle_id: usize) {
-        if let Some((_, backtraces)) = self.handle_backtraces.get_mut(&entity_id) {
-            assert!(backtraces.remove(&handle_id).is_some());
-            if backtraces.is_empty() {
-                self.handle_backtraces.remove(&entity_id);
-            }
-        }
-    }
-
-    pub fn assert_dropped(&mut self, entity_id: usize) {
-        if let Some((type_name, backtraces)) = self.handle_backtraces.get_mut(&entity_id) {
-            for trace in backtraces.values_mut().flatten() {
-                trace.resolve();
-                eprintln!("{:?}", crate::util::CwdBacktrace(trace));
-            }
-
-            let hint = if *LEAK_BACKTRACE {
-                ""
-            } else {
-                " – set LEAK_BACKTRACE=1 for more information"
-            };
-
-            panic!(
-                "{} handles to {} {} still exist{}",
-                backtraces.len(),
-                type_name.unwrap_or("entity"),
-                entity_id,
-                hint
-            );
-        }
-    }
-
-    pub fn detect(&mut self) {
-        let mut found_leaks = false;
-        for (id, (type_name, backtraces)) in self.handle_backtraces.iter_mut() {
-            eprintln!(
-                "leaked {} handles to {} {}",
-                backtraces.len(),
-                type_name.unwrap_or("entity"),
-                id
-            );
-            for trace in backtraces.values_mut().flatten() {
-                trace.resolve();
-                eprintln!("{:?}", crate::util::CwdBacktrace(trace));
-            }
-            found_leaks = true;
-        }
-
-        let hint = if *LEAK_BACKTRACE {
-            ""
-        } else {
-            " – set LEAK_BACKTRACE=1 for more information"
-        };
-        assert!(!found_leaks, "detected leaked handles{}", hint);
-    }
-}

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

@@ -1,661 +0,0 @@
-use crate::{
-    executor,
-    geometry::vector::Vector2F,
-    keymap_matcher::{Binding, Keystroke},
-    platform,
-    platform::{Event, InputHandler, KeyDownEvent, Platform},
-    Action, AnyWindowHandle, AppContext, BorrowAppContext, BorrowWindowContext, Entity, FontCache,
-    Handle, ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle,
-    WeakHandle, WindowContext, WindowHandle,
-};
-use collections::BTreeMap;
-use futures::Future;
-use itertools::Itertools;
-use parking_lot::{Mutex, RwLock};
-use smallvec::SmallVec;
-use smol::stream::StreamExt;
-use std::{
-    any::Any,
-    cell::RefCell,
-    mem,
-    path::PathBuf,
-    rc::Rc,
-    sync::{
-        atomic::{AtomicUsize, Ordering},
-        Arc,
-    },
-    time::Duration,
-};
-
-use super::{
-    ref_counts::LeakDetector, window_input_handler::WindowInputHandler, AsyncAppContext, RefCounts,
-};
-
-#[derive(Clone)]
-pub struct TestAppContext {
-    cx: Rc<RefCell<AppContext>>,
-    foreground_platform: Rc<platform::test::ForegroundPlatform>,
-    condition_duration: Option<Duration>,
-    pub function_name: String,
-    assertion_context: AssertionContextManager,
-}
-
-impl TestAppContext {
-    pub fn new(
-        foreground_platform: Rc<platform::test::ForegroundPlatform>,
-        platform: Arc<dyn Platform>,
-        foreground: Rc<executor::Foreground>,
-        background: Arc<executor::Background>,
-        font_cache: Arc<FontCache>,
-        leak_detector: Arc<Mutex<LeakDetector>>,
-        first_entity_id: usize,
-        function_name: String,
-    ) -> Self {
-        let mut cx = AppContext::new(
-            foreground,
-            background,
-            platform,
-            foreground_platform.clone(),
-            font_cache,
-            util::http::FakeHttpClient::with_404_response(),
-            RefCounts::new(leak_detector),
-            (),
-        );
-        cx.next_id = first_entity_id;
-        let cx = TestAppContext {
-            cx: Rc::new(RefCell::new(cx)),
-            foreground_platform,
-            condition_duration: None,
-            function_name,
-            assertion_context: AssertionContextManager::new(),
-        };
-        cx.cx.borrow_mut().weak_self = Some(Rc::downgrade(&cx.cx));
-        cx
-    }
-
-    pub fn dispatch_action<A: Action>(&mut self, window: AnyWindowHandle, action: A) {
-        self.update_window(window, |window| {
-            window.dispatch_action(window.focused_view_id(), &action);
-        })
-        .expect("window not found");
-    }
-
-    pub fn available_actions(
-        &self,
-        window: AnyWindowHandle,
-        view_id: usize,
-    ) -> Vec<(&'static str, Box<dyn Action>, SmallVec<[Binding; 1]>)> {
-        self.read_window(window, |cx| cx.available_actions(view_id))
-            .unwrap_or_default()
-    }
-
-    pub fn dispatch_global_action<A: Action>(&mut self, action: A) {
-        self.update(|cx| cx.dispatch_global_action_any(&action));
-    }
-
-    pub fn dispatch_keystroke(
-        &mut self,
-        window: AnyWindowHandle,
-        keystroke: Keystroke,
-        is_held: bool,
-    ) {
-        let handled = window.update(self, |cx| {
-            if cx.dispatch_keystroke(&keystroke) {
-                return true;
-            }
-
-            if cx.dispatch_event(
-                Event::KeyDown(KeyDownEvent {
-                    keystroke: keystroke.clone(),
-                    is_held,
-                }),
-                false,
-            ) {
-                return true;
-            }
-
-            false
-        });
-
-        if !handled && !keystroke.cmd && !keystroke.ctrl {
-            WindowInputHandler {
-                app: self.cx.clone(),
-                window,
-            }
-            .replace_text_in_range(None, &keystroke.key)
-        }
-    }
-
-    pub fn read_window<T, F: FnOnce(&WindowContext) -> T>(
-        &self,
-        window: AnyWindowHandle,
-        callback: F,
-    ) -> Option<T> {
-        self.cx.borrow().read_window(window, callback)
-    }
-
-    pub fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
-        &mut self,
-        window: AnyWindowHandle,
-        callback: F,
-    ) -> Option<T> {
-        self.cx.borrow_mut().update_window(window, callback)
-    }
-
-    pub fn add_model<T, F>(&mut self, build_model: F) -> ModelHandle<T>
-    where
-        T: Entity,
-        F: FnOnce(&mut ModelContext<T>) -> T,
-    {
-        self.cx.borrow_mut().add_model(build_model)
-    }
-
-    pub fn add_window<V, F>(&mut self, build_root_view: F) -> WindowHandle<V>
-    where
-        V: View,
-        F: FnOnce(&mut ViewContext<V>) -> V,
-    {
-        let window = self
-            .cx
-            .borrow_mut()
-            .add_window(Default::default(), build_root_view);
-        window.simulate_activation(self);
-        window
-    }
-
-    pub fn observe_global<E, F>(&mut self, callback: F) -> Subscription
-    where
-        E: Any,
-        F: 'static + FnMut(&mut AppContext),
-    {
-        self.cx.borrow_mut().observe_global::<E, F>(callback)
-    }
-
-    pub fn set_global<T: 'static>(&mut self, state: T) {
-        self.cx.borrow_mut().set_global(state);
-    }
-
-    pub fn subscribe_global<E, F>(&mut self, callback: F) -> Subscription
-    where
-        E: Any,
-        F: 'static + FnMut(&E, &mut AppContext),
-    {
-        self.cx.borrow_mut().subscribe_global(callback)
-    }
-
-    pub fn windows(&self) -> Vec<AnyWindowHandle> {
-        self.cx.borrow().windows().collect()
-    }
-
-    pub fn remove_all_windows(&mut self) {
-        self.update(|cx| cx.windows.clear());
-    }
-
-    pub fn read<T, F: FnOnce(&AppContext) -> T>(&self, callback: F) -> T {
-        callback(&*self.cx.borrow())
-    }
-
-    pub fn update<T, F: FnOnce(&mut AppContext) -> T>(&mut self, callback: F) -> T {
-        let mut state = self.cx.borrow_mut();
-        // Don't increment pending flushes in order for effects to be flushed before the callback
-        // completes, which is helpful in tests.
-        let result = callback(&mut *state);
-        // Flush effects after the callback just in case there are any. This can happen in edge
-        // cases such as the closure dropping handles.
-        state.flush_effects();
-        result
-    }
-
-    pub fn to_async(&self) -> AsyncAppContext {
-        AsyncAppContext(self.cx.clone())
-    }
-
-    pub fn font_cache(&self) -> Arc<FontCache> {
-        self.cx.borrow().font_cache.clone()
-    }
-
-    pub fn foreground_platform(&self) -> Rc<platform::test::ForegroundPlatform> {
-        self.foreground_platform.clone()
-    }
-
-    pub fn platform(&self) -> Arc<dyn platform::Platform> {
-        self.cx.borrow().platform.clone()
-    }
-
-    pub fn foreground(&self) -> Rc<executor::Foreground> {
-        self.cx.borrow().foreground().clone()
-    }
-
-    pub fn background(&self) -> Arc<executor::Background> {
-        self.cx.borrow().background().clone()
-    }
-
-    pub fn spawn<F, Fut, T>(&self, f: F) -> Task<T>
-    where
-        F: FnOnce(AsyncAppContext) -> Fut,
-        Fut: 'static + Future<Output = T>,
-        T: 'static,
-    {
-        let foreground = self.foreground();
-        let future = f(self.to_async());
-        let cx = self.to_async();
-        foreground.spawn(async move {
-            let result = future.await;
-            cx.0.borrow_mut().flush_effects();
-            result
-        })
-    }
-
-    pub fn simulate_new_path_selection(&self, result: impl FnOnce(PathBuf) -> Option<PathBuf>) {
-        self.foreground_platform.simulate_new_path_selection(result);
-    }
-
-    pub fn did_prompt_for_new_path(&self) -> bool {
-        self.foreground_platform.as_ref().did_prompt_for_new_path()
-    }
-
-    pub fn leak_detector(&self) -> Arc<Mutex<LeakDetector>> {
-        self.cx.borrow().leak_detector()
-    }
-
-    pub fn assert_dropped(&self, handle: impl WeakHandle) {
-        self.cx
-            .borrow()
-            .leak_detector()
-            .lock()
-            .assert_dropped(handle.id())
-    }
-
-    /// Drop a handle, assuming it is the last. If it is not the last, panic with debug information about
-    /// where the stray handles were created.
-    pub fn drop_last<T, W: WeakHandle, H: Handle<T, Weak = W>>(&mut self, handle: H) {
-        let weak = handle.downgrade();
-        self.update(|_| drop(handle));
-        self.assert_dropped(weak);
-    }
-
-    pub fn set_condition_duration(&mut self, duration: Option<Duration>) {
-        self.condition_duration = duration;
-    }
-
-    pub fn condition_duration(&self) -> Duration {
-        self.condition_duration.unwrap_or_else(|| {
-            if std::env::var("CI").is_ok() {
-                Duration::from_secs(2)
-            } else {
-                Duration::from_millis(500)
-            }
-        })
-    }
-
-    pub fn assert_clipboard_content(&mut self, expected_content: Option<&str>) {
-        self.update(|cx| {
-            let actual_content = cx.read_from_clipboard().map(|item| item.text().to_owned());
-            let expected_content = expected_content.map(|content| content.to_owned());
-            assert_eq!(actual_content, expected_content);
-        })
-    }
-
-    pub fn add_assertion_context(&self, context: String) -> ContextHandle {
-        self.assertion_context.add_context(context)
-    }
-
-    pub fn assertion_context(&self) -> String {
-        self.assertion_context.context()
-    }
-}
-
-impl BorrowAppContext for TestAppContext {
-    fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
-        self.cx.borrow().read_with(f)
-    }
-
-    fn update<T, F: FnOnce(&mut AppContext) -> T>(&mut self, f: F) -> T {
-        self.cx.borrow_mut().update(f)
-    }
-}
-
-impl BorrowWindowContext for TestAppContext {
-    type Result<T> = T;
-
-    fn read_window<T, F: FnOnce(&WindowContext) -> T>(&self, window: AnyWindowHandle, f: F) -> T {
-        self.cx
-            .borrow()
-            .read_window(window, f)
-            .expect("window was closed")
-    }
-
-    fn read_window_optional<T, F>(&self, window: AnyWindowHandle, f: F) -> Option<T>
-    where
-        F: FnOnce(&WindowContext) -> Option<T>,
-    {
-        BorrowWindowContext::read_window(self, window, f)
-    }
-
-    fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
-        &mut self,
-        window: AnyWindowHandle,
-        f: F,
-    ) -> T {
-        self.cx
-            .borrow_mut()
-            .update_window(window, f)
-            .expect("window was closed")
-    }
-
-    fn update_window_optional<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Option<T>
-    where
-        F: FnOnce(&mut WindowContext) -> Option<T>,
-    {
-        BorrowWindowContext::update_window(self, window, f)
-    }
-}
-
-impl<T: Entity> ModelHandle<T> {
-    pub fn next_notification(&self, cx: &TestAppContext) -> impl Future<Output = ()> {
-        let (tx, mut rx) = futures::channel::mpsc::unbounded();
-        let mut cx = cx.cx.borrow_mut();
-        let subscription = cx.observe(self, move |_, _| {
-            tx.unbounded_send(()).ok();
-        });
-
-        let duration = if std::env::var("CI").is_ok() {
-            Duration::from_secs(5)
-        } else {
-            Duration::from_secs(1)
-        };
-
-        let executor = cx.background().clone();
-        async move {
-            executor.start_waiting();
-            let notification = crate::util::timeout(duration, rx.next())
-                .await
-                .expect("next notification timed out");
-            drop(subscription);
-            notification.expect("model dropped while test was waiting for its next notification")
-        }
-    }
-
-    pub fn next_event(&self, cx: &TestAppContext) -> impl Future<Output = T::Event>
-    where
-        T::Event: Clone,
-    {
-        let (tx, mut rx) = futures::channel::mpsc::unbounded();
-        let mut cx = cx.cx.borrow_mut();
-        let subscription = cx.subscribe(self, move |_, event, _| {
-            tx.unbounded_send(event.clone()).ok();
-        });
-
-        let duration = if std::env::var("CI").is_ok() {
-            Duration::from_secs(5)
-        } else {
-            Duration::from_secs(1)
-        };
-
-        cx.foreground.start_waiting();
-        async move {
-            let event = crate::util::timeout(duration, rx.next())
-                .await
-                .expect("next event timed out");
-            drop(subscription);
-            event.expect("model dropped while test was waiting for its next event")
-        }
-    }
-
-    pub fn condition(
-        &self,
-        cx: &TestAppContext,
-        mut predicate: impl FnMut(&T, &AppContext) -> bool,
-    ) -> impl Future<Output = ()> {
-        let (tx, mut rx) = futures::channel::mpsc::unbounded();
-
-        let mut cx = cx.cx.borrow_mut();
-        let subscriptions = (
-            cx.observe(self, {
-                let tx = tx.clone();
-                move |_, _| {
-                    tx.unbounded_send(()).ok();
-                }
-            }),
-            cx.subscribe(self, {
-                move |_, _, _| {
-                    tx.unbounded_send(()).ok();
-                }
-            }),
-        );
-
-        let cx = cx.weak_self.as_ref().unwrap().upgrade().unwrap();
-        let handle = self.downgrade();
-        let duration = if std::env::var("CI").is_ok() {
-            Duration::from_secs(5)
-        } else {
-            Duration::from_secs(1)
-        };
-
-        async move {
-            crate::util::timeout(duration, async move {
-                loop {
-                    {
-                        let cx = cx.borrow();
-                        let cx = &*cx;
-                        if predicate(
-                            handle
-                                .upgrade(cx)
-                                .expect("model dropped with pending condition")
-                                .read(cx),
-                            cx,
-                        ) {
-                            break;
-                        }
-                    }
-
-                    cx.borrow().foreground().start_waiting();
-                    rx.next()
-                        .await
-                        .expect("model dropped with pending condition");
-                    cx.borrow().foreground().finish_waiting();
-                }
-            })
-            .await
-            .expect("condition timed out");
-            drop(subscriptions);
-        }
-    }
-}
-
-impl AnyWindowHandle {
-    pub fn has_pending_prompt(&self, cx: &mut TestAppContext) -> bool {
-        let window = self.platform_window_mut(cx);
-        let prompts = window.pending_prompts.borrow_mut();
-        !prompts.is_empty()
-    }
-
-    pub fn current_title(&self, cx: &mut TestAppContext) -> Option<String> {
-        self.platform_window_mut(cx).title.clone()
-    }
-
-    pub fn simulate_close(&self, cx: &mut TestAppContext) -> bool {
-        let handler = self.platform_window_mut(cx).should_close_handler.take();
-        if let Some(mut handler) = handler {
-            let should_close = handler();
-            self.platform_window_mut(cx).should_close_handler = Some(handler);
-            should_close
-        } else {
-            false
-        }
-    }
-
-    pub fn simulate_resize(&self, size: Vector2F, cx: &mut TestAppContext) {
-        let mut window = self.platform_window_mut(cx);
-        window.size = size;
-        let mut handlers = mem::take(&mut window.resize_handlers);
-        drop(window);
-        for handler in &mut handlers {
-            handler();
-        }
-        self.platform_window_mut(cx).resize_handlers = handlers;
-    }
-
-    pub fn is_edited(&self, cx: &mut TestAppContext) -> bool {
-        self.platform_window_mut(cx).edited
-    }
-
-    pub fn simulate_prompt_answer(&self, answer: usize, cx: &mut TestAppContext) {
-        use postage::prelude::Sink as _;
-
-        let mut done_tx = self
-            .platform_window_mut(cx)
-            .pending_prompts
-            .borrow_mut()
-            .pop_front()
-            .expect("prompt was not called");
-        done_tx.try_send(answer).ok();
-    }
-
-    fn platform_window_mut<'a>(
-        &self,
-        cx: &'a mut TestAppContext,
-    ) -> std::cell::RefMut<'a, platform::test::Window> {
-        std::cell::RefMut::map(cx.cx.borrow_mut(), |state| {
-            let window = state.windows.get_mut(&self).unwrap();
-            let test_window = window
-                .platform_window
-                .as_any_mut()
-                .downcast_mut::<platform::test::Window>()
-                .unwrap();
-            test_window
-        })
-    }
-}
-
-impl<T: View> ViewHandle<T> {
-    pub fn next_notification(&self, cx: &TestAppContext) -> impl Future<Output = ()> {
-        use postage::prelude::{Sink as _, Stream as _};
-
-        let (mut tx, mut rx) = postage::mpsc::channel(1);
-        let mut cx = cx.cx.borrow_mut();
-        let subscription = cx.observe(self, move |_, _| {
-            tx.try_send(()).ok();
-        });
-
-        let duration = if std::env::var("CI").is_ok() {
-            Duration::from_secs(5)
-        } else {
-            Duration::from_secs(1)
-        };
-
-        async move {
-            let notification = crate::util::timeout(duration, rx.recv())
-                .await
-                .expect("next notification timed out");
-            drop(subscription);
-            notification.expect("model dropped while test was waiting for its next notification")
-        }
-    }
-
-    pub fn condition(
-        &self,
-        cx: &TestAppContext,
-        mut predicate: impl FnMut(&T, &AppContext) -> bool,
-    ) -> impl Future<Output = ()> {
-        use postage::prelude::{Sink as _, Stream as _};
-
-        let (tx, mut rx) = postage::mpsc::channel(1024);
-        let timeout_duration = cx.condition_duration();
-
-        let mut cx = cx.cx.borrow_mut();
-        let subscriptions = (
-            cx.observe(self, {
-                let mut tx = tx.clone();
-                move |_, _| {
-                    tx.blocking_send(()).ok();
-                }
-            }),
-            cx.subscribe(self, {
-                let mut tx = tx.clone();
-                move |_, _, _| {
-                    tx.blocking_send(()).ok();
-                }
-            }),
-        );
-
-        let cx = cx.weak_self.as_ref().unwrap().upgrade().unwrap();
-        let handle = self.downgrade();
-
-        async move {
-            crate::util::timeout(timeout_duration, async move {
-                loop {
-                    {
-                        let cx = cx.borrow();
-                        let cx = &*cx;
-                        if predicate(
-                            handle
-                                .upgrade(cx)
-                                .expect("view dropped with pending condition")
-                                .read(cx),
-                            cx,
-                        ) {
-                            break;
-                        }
-                    }
-
-                    cx.borrow().foreground().start_waiting();
-                    rx.recv()
-                        .await
-                        .expect("view dropped with pending condition");
-                    cx.borrow().foreground().finish_waiting();
-                }
-            })
-            .await
-            .expect("condition timed out");
-            drop(subscriptions);
-        }
-    }
-}
-
-/// Tracks string context to be printed when assertions fail.
-/// Often this is done by storing a context string in the manager and returning the handle.
-#[derive(Clone)]
-pub struct AssertionContextManager {
-    id: Arc<AtomicUsize>,
-    contexts: Arc<RwLock<BTreeMap<usize, String>>>,
-}
-
-impl AssertionContextManager {
-    pub fn new() -> Self {
-        Self {
-            id: Arc::new(AtomicUsize::new(0)),
-            contexts: Arc::new(RwLock::new(BTreeMap::new())),
-        }
-    }
-
-    pub fn add_context(&self, context: String) -> ContextHandle {
-        let id = self.id.fetch_add(1, Ordering::Relaxed);
-        let mut contexts = self.contexts.write();
-        contexts.insert(id, context);
-        ContextHandle {
-            id,
-            manager: self.clone(),
-        }
-    }
-
-    pub fn context(&self) -> String {
-        let contexts = self.contexts.read();
-        format!("\n{}\n", contexts.values().join("\n"))
-    }
-}
-
-/// Used to track the lifetime of a piece of context so that it can be provided when an assertion fails.
-/// For example, in the EditorTestContext, `set_state` returns a context handle so that if an assertion fails,
-/// the state that was set initially for the failure can be printed in the error message
-pub struct ContextHandle {
-    id: usize,
-    manager: AssertionContextManager,
-}
-
-impl Drop for ContextHandle {
-    fn drop(&mut self) {
-        let mut contexts = self.manager.contexts.write();
-        contexts.remove(&self.id);
-    }
-}

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

@@ -1,1767 +0,0 @@
-use crate::{
-    elements::AnyRootElement,
-    fonts::{TextStyle, TextStyleRefinement},
-    geometry::{rect::RectF, Size},
-    json::ToJson,
-    keymap_matcher::{Binding, KeymapContext, Keystroke, MatchResult},
-    platform::{
-        self, Appearance, CursorStyle, Event, KeyDownEvent, KeyUpEvent, ModifiersChangedEvent,
-        MouseButton, MouseMovedEvent, PromptLevel, WindowBounds,
-    },
-    scene::{
-        CursorRegion, EventHandler, MouseClick, MouseClickOut, MouseDown, MouseDownOut, MouseDrag,
-        MouseEvent, MouseHover, MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut,
-        Scene,
-    },
-    text_layout::TextLayoutCache,
-    util::post_inc,
-    Action, AnyView, AnyViewHandle, AnyWindowHandle, AppContext, BorrowAppContext,
-    BorrowWindowContext, Effect, Element, Entity, Handle, MouseRegion, MouseRegionId, SceneBuilder,
-    Subscription, View, ViewContext, ViewHandle, WindowInvalidation,
-};
-use anyhow::{anyhow, bail, Result};
-use collections::{HashMap, HashSet};
-use pathfinder_geometry::vector::{vec2f, Vector2F};
-use postage::oneshot;
-use serde_json::json;
-use smallvec::SmallVec;
-use sqlez::{
-    bindable::{Bind, Column, StaticColumnCount},
-    statement::Statement,
-};
-use std::{
-    any::{type_name, Any, TypeId},
-    mem,
-    ops::{Deref, DerefMut, Range, Sub},
-    sync::Arc,
-};
-use taffy::{
-    tree::{Measurable, MeasureFunc},
-    Taffy,
-};
-use util::ResultExt;
-use uuid::Uuid;
-
-use super::{Reference, ViewMetadata};
-
-pub struct Window {
-    layout_engines: Vec<LayoutEngine>,
-    pub(crate) root_view: Option<AnyViewHandle>,
-    pub(crate) focused_view_id: Option<usize>,
-    pub(crate) parents: HashMap<usize, usize>,
-    pub(crate) is_active: bool,
-    pub(crate) is_fullscreen: bool,
-    inspector_enabled: bool,
-    pub(crate) invalidation: Option<WindowInvalidation>,
-    pub(crate) platform_window: Box<dyn platform::Window>,
-    pub(crate) rendered_views: HashMap<usize, Box<dyn AnyRootElement>>,
-    scene: SceneBuilder,
-    pub(crate) text_style_stack: Vec<TextStyle>,
-    pub(crate) theme_stack: Vec<Arc<dyn Any + Send + Sync>>,
-    pub(crate) new_parents: HashMap<usize, usize>,
-    pub(crate) views_to_notify_if_ancestors_change: HashMap<usize, SmallVec<[usize; 2]>>,
-    titlebar_height: f32,
-    appearance: Appearance,
-    cursor_regions: Vec<CursorRegion>,
-    mouse_regions: Vec<(MouseRegion, usize)>,
-    event_handlers: Vec<EventHandler>,
-    last_mouse_moved_event: Option<Event>,
-    last_mouse_position: Vector2F,
-    pressed_buttons: HashSet<MouseButton>,
-    pub(crate) hovered_region_ids: Vec<MouseRegionId>,
-    pub(crate) clicked_region_ids: Vec<MouseRegionId>,
-    pub(crate) clicked_region: Option<(MouseRegionId, MouseButton)>,
-    text_layout_cache: Arc<TextLayoutCache>,
-    refreshing: bool,
-}
-
-impl Window {
-    pub fn new<V, F>(
-        handle: AnyWindowHandle,
-        platform_window: Box<dyn platform::Window>,
-        cx: &mut AppContext,
-        build_view: F,
-    ) -> Self
-    where
-        V: View,
-        F: FnOnce(&mut ViewContext<V>) -> V,
-    {
-        let titlebar_height = platform_window.titlebar_height();
-        let appearance = platform_window.appearance();
-        let mut window = Self {
-            layout_engines: Vec::new(),
-            root_view: None,
-            focused_view_id: None,
-            parents: Default::default(),
-            is_active: false,
-            invalidation: None,
-            is_fullscreen: false,
-            inspector_enabled: false,
-            platform_window,
-            rendered_views: Default::default(),
-            scene: SceneBuilder::new(),
-            text_style_stack: Vec::new(),
-            theme_stack: Vec::new(),
-            new_parents: HashMap::default(),
-            views_to_notify_if_ancestors_change: HashMap::default(),
-            cursor_regions: Default::default(),
-            mouse_regions: Default::default(),
-            event_handlers: Default::default(),
-            text_layout_cache: Arc::new(TextLayoutCache::new(cx.font_system.clone())),
-            last_mouse_moved_event: None,
-            last_mouse_position: Vector2F::zero(),
-            pressed_buttons: Default::default(),
-            hovered_region_ids: Default::default(),
-            clicked_region_ids: Default::default(),
-            clicked_region: None,
-            titlebar_height,
-            appearance,
-            refreshing: false,
-        };
-
-        let mut window_context = WindowContext::mutable(cx, &mut window, handle);
-        let root_view = window_context.add_view(|cx| build_view(cx));
-        if let Some(invalidation) = window_context.window.invalidation.take() {
-            window_context.invalidate(invalidation, appearance);
-        }
-        window.focused_view_id = Some(root_view.id());
-        window.root_view = Some(root_view.into_any());
-        window
-    }
-
-    pub fn root_view(&self) -> &AnyViewHandle {
-        &self
-            .root_view
-            .as_ref()
-            .expect("root_view called during window construction")
-    }
-
-    pub fn take_event_handlers(&mut self) -> Vec<EventHandler> {
-        mem::take(&mut self.event_handlers)
-    }
-}
-
-pub struct WindowContext<'a> {
-    pub(crate) app_context: Reference<'a, AppContext>,
-    pub(crate) window: Reference<'a, Window>,
-    pub(crate) window_handle: AnyWindowHandle,
-    pub(crate) removed: bool,
-}
-
-impl Deref for WindowContext<'_> {
-    type Target = AppContext;
-
-    fn deref(&self) -> &Self::Target {
-        &self.app_context
-    }
-}
-
-impl DerefMut for WindowContext<'_> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.app_context
-    }
-}
-
-impl BorrowAppContext for WindowContext<'_> {
-    fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
-        self.app_context.read_with(f)
-    }
-
-    fn update<T, F: FnOnce(&mut AppContext) -> T>(&mut self, f: F) -> T {
-        self.app_context.update(f)
-    }
-}
-
-impl BorrowWindowContext for WindowContext<'_> {
-    type Result<T> = T;
-
-    fn read_window<T, F: FnOnce(&WindowContext) -> T>(&self, handle: AnyWindowHandle, f: F) -> T {
-        if self.window_handle == handle {
-            f(self)
-        } else {
-            panic!("read_with called with id of window that does not belong to this context")
-        }
-    }
-
-    fn read_window_optional<T, F>(&self, window: AnyWindowHandle, f: F) -> Option<T>
-    where
-        F: FnOnce(&WindowContext) -> Option<T>,
-    {
-        BorrowWindowContext::read_window(self, window, f)
-    }
-
-    fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
-        &mut self,
-        handle: AnyWindowHandle,
-        f: F,
-    ) -> T {
-        if self.window_handle == handle {
-            f(self)
-        } else {
-            panic!("update called with id of window that does not belong to this context")
-        }
-    }
-
-    fn update_window_optional<T, F>(&mut self, handle: AnyWindowHandle, f: F) -> Option<T>
-    where
-        F: FnOnce(&mut WindowContext) -> Option<T>,
-    {
-        BorrowWindowContext::update_window(self, handle, f)
-    }
-}
-
-impl<'a> WindowContext<'a> {
-    pub fn mutable(
-        app_context: &'a mut AppContext,
-        window: &'a mut Window,
-        handle: AnyWindowHandle,
-    ) -> Self {
-        Self {
-            app_context: Reference::Mutable(app_context),
-            window: Reference::Mutable(window),
-            window_handle: handle,
-            removed: false,
-        }
-    }
-
-    pub fn immutable(
-        app_context: &'a AppContext,
-        window: &'a Window,
-        handle: AnyWindowHandle,
-    ) -> Self {
-        Self {
-            app_context: Reference::Immutable(app_context),
-            window: Reference::Immutable(window),
-            window_handle: handle,
-            removed: false,
-        }
-    }
-
-    pub fn repaint(&mut self) {
-        let window = self.window();
-        self.pending_effects
-            .push_back(Effect::RepaintWindow { window });
-    }
-
-    pub fn scene(&mut self) -> &mut SceneBuilder {
-        &mut self.window.scene
-    }
-
-    pub fn enable_inspector(&mut self) {
-        self.window.inspector_enabled = true;
-    }
-
-    pub fn is_inspector_enabled(&self) -> bool {
-        self.window.inspector_enabled
-    }
-
-    pub fn is_mouse_down(&self, button: MouseButton) -> bool {
-        self.window.pressed_buttons.contains(&button)
-    }
-
-    pub fn rem_size(&self) -> f32 {
-        16.
-    }
-
-    pub fn layout_engine(&mut self) -> Option<&mut LayoutEngine> {
-        self.window.layout_engines.last_mut()
-    }
-
-    pub fn push_layout_engine(&mut self, engine: LayoutEngine) {
-        self.window.layout_engines.push(engine);
-    }
-
-    pub fn pop_layout_engine(&mut self) -> Option<LayoutEngine> {
-        self.window.layout_engines.pop()
-    }
-
-    pub fn remove_window(&mut self) {
-        self.removed = true;
-    }
-
-    pub fn window(&self) -> AnyWindowHandle {
-        self.window_handle
-    }
-
-    pub fn app_context(&mut self) -> &mut AppContext {
-        &mut self.app_context
-    }
-
-    pub fn root_view(&self) -> &AnyViewHandle {
-        self.window.root_view()
-    }
-
-    pub fn window_size(&self) -> Vector2F {
-        self.window.platform_window.content_size()
-    }
-
-    pub fn mouse_position(&self) -> Vector2F {
-        self.window.platform_window.mouse_position()
-    }
-
-    pub fn refreshing(&self) -> bool {
-        self.window.refreshing
-    }
-
-    pub fn text_layout_cache(&self) -> &Arc<TextLayoutCache> {
-        &self.window.text_layout_cache
-    }
-
-    pub(crate) fn update_any_view<F, T>(&mut self, view_id: usize, f: F) -> Option<T>
-    where
-        F: FnOnce(&mut dyn AnyView, &mut Self) -> T,
-    {
-        let handle = self.window_handle;
-        let mut view = self.views.remove(&(handle, view_id))?;
-        let result = f(view.as_mut(), self);
-        self.views.insert((handle, view_id), view);
-        Some(result)
-    }
-
-    pub(crate) fn update_view<V: 'static, S>(
-        &mut self,
-        handle: &ViewHandle<V>,
-        update: &mut dyn FnMut(&mut V, &mut ViewContext<V>) -> S,
-    ) -> S {
-        self.update_any_view(handle.view_id, |view, cx| {
-            let mut cx = ViewContext::mutable(cx, handle.view_id);
-            update(
-                view.as_any_mut()
-                    .downcast_mut()
-                    .expect("downcast is type safe"),
-                &mut cx,
-            )
-        })
-        .expect("view is already on the stack")
-    }
-
-    pub fn defer(&mut self, callback: impl 'static + FnOnce(&mut WindowContext)) {
-        let handle = self.window_handle;
-        self.app_context.defer(move |cx| {
-            cx.update_window(handle, |cx| callback(cx));
-        })
-    }
-
-    pub fn update_global<T, F, U>(&mut self, update: F) -> U
-    where
-        T: 'static,
-        F: FnOnce(&mut T, &mut Self) -> U,
-    {
-        AppContext::update_global_internal(self, |global, cx| update(global, cx))
-    }
-
-    pub fn update_default_global<T, F, U>(&mut self, update: F) -> U
-    where
-        T: 'static + Default,
-        F: FnOnce(&mut T, &mut Self) -> U,
-    {
-        AppContext::update_default_global_internal(self, |global, cx| update(global, cx))
-    }
-
-    pub fn subscribe<E, H, F>(&mut self, handle: &H, mut callback: F) -> Subscription
-    where
-        E: Entity,
-        E::Event: 'static,
-        H: Handle<E>,
-        F: 'static + FnMut(H, &E::Event, &mut WindowContext),
-    {
-        self.subscribe_internal(handle, move |emitter, event, cx| {
-            callback(emitter, event, cx);
-            true
-        })
-    }
-
-    pub fn subscribe_internal<E, H, F>(&mut self, handle: &H, mut callback: F) -> Subscription
-    where
-        E: Entity,
-        E::Event: 'static,
-        H: Handle<E>,
-        F: 'static + FnMut(H, &E::Event, &mut WindowContext) -> bool,
-    {
-        let window_handle = self.window_handle;
-        self.app_context
-            .subscribe_internal(handle, move |emitter, event, cx| {
-                cx.update_window(window_handle, |cx| callback(emitter, event, cx))
-                    .unwrap_or(false)
-            })
-    }
-
-    pub(crate) fn observe_window_activation<F>(&mut self, callback: F) -> Subscription
-    where
-        F: 'static + FnMut(bool, &mut WindowContext) -> bool,
-    {
-        let handle = self.window_handle;
-        let subscription_id = post_inc(&mut self.next_subscription_id);
-        self.pending_effects
-            .push_back(Effect::WindowActivationObservation {
-                window: handle,
-                subscription_id,
-                callback: Box::new(callback),
-            });
-        Subscription::WindowActivationObservation(
-            self.window_activation_observations
-                .subscribe(handle, subscription_id),
-        )
-    }
-
-    pub(crate) fn observe_fullscreen<F>(&mut self, callback: F) -> Subscription
-    where
-        F: 'static + FnMut(bool, &mut WindowContext) -> bool,
-    {
-        let window = self.window_handle;
-        let subscription_id = post_inc(&mut self.next_subscription_id);
-        self.pending_effects
-            .push_back(Effect::WindowFullscreenObservation {
-                window,
-                subscription_id,
-                callback: Box::new(callback),
-            });
-        Subscription::WindowActivationObservation(
-            self.window_activation_observations
-                .subscribe(window, subscription_id),
-        )
-    }
-
-    pub(crate) fn observe_window_bounds<F>(&mut self, callback: F) -> Subscription
-    where
-        F: 'static + FnMut(WindowBounds, Uuid, &mut WindowContext) -> bool,
-    {
-        let window = self.window_handle;
-        let subscription_id = post_inc(&mut self.next_subscription_id);
-        self.pending_effects
-            .push_back(Effect::WindowBoundsObservation {
-                window,
-                subscription_id,
-                callback: Box::new(callback),
-            });
-        Subscription::WindowBoundsObservation(
-            self.window_bounds_observations
-                .subscribe(window, subscription_id),
-        )
-    }
-
-    pub fn observe_keystrokes<F>(&mut self, callback: F) -> Subscription
-    where
-        F: 'static
-            + FnMut(&Keystroke, &MatchResult, Option<&Box<dyn Action>>, &mut WindowContext) -> bool,
-    {
-        let window = self.window_handle;
-        let subscription_id = post_inc(&mut self.next_subscription_id);
-        self.keystroke_observations
-            .add_callback(window, subscription_id, Box::new(callback));
-        Subscription::KeystrokeObservation(
-            self.keystroke_observations
-                .subscribe(window, subscription_id),
-        )
-    }
-
-    pub(crate) fn available_actions(
-        &self,
-        view_id: usize,
-    ) -> Vec<(&'static str, Box<dyn Action>, SmallVec<[Binding; 1]>)> {
-        let handle = self.window_handle;
-        let mut contexts = Vec::new();
-        let mut handler_depths_by_action_id = HashMap::<TypeId, usize>::default();
-        for (depth, view_id) in self.ancestors(view_id).enumerate() {
-            if let Some(view_metadata) = self.views_metadata.get(&(handle, view_id)) {
-                contexts.push(view_metadata.keymap_context.clone());
-                if let Some(actions) = self.actions.get(&view_metadata.type_id) {
-                    handler_depths_by_action_id
-                        .extend(actions.keys().copied().map(|action_id| (action_id, depth)));
-                }
-            } else {
-                log::error!(
-                    "view {} not found when computing available actions",
-                    view_id
-                );
-            }
-        }
-
-        handler_depths_by_action_id.extend(
-            self.global_actions
-                .keys()
-                .copied()
-                .map(|action_id| (action_id, contexts.len())),
-        );
-
-        self.action_deserializers
-            .iter()
-            .filter_map(move |(name, (action_id, deserialize))| {
-                if let Some(action_depth) = handler_depths_by_action_id.get(action_id).copied() {
-                    let action = deserialize(serde_json::Value::Object(Default::default())).ok()?;
-                    let bindings = self
-                        .keystroke_matcher
-                        .bindings_for_action(*action_id)
-                        .filter(|b| {
-                            action.eq(b.action())
-                                && (0..=action_depth)
-                                    .any(|depth| b.match_context(&contexts[depth..]))
-                        })
-                        .cloned()
-                        .collect();
-                    Some((*name, action, bindings))
-                } else {
-                    None
-                }
-            })
-            .collect()
-    }
-
-    pub(crate) fn dispatch_keystroke(&mut self, keystroke: &Keystroke) -> bool {
-        let handle = self.window_handle;
-        if let Some(focused_view_id) = self.focused_view_id() {
-            let dispatch_path = self
-                .ancestors(focused_view_id)
-                .filter_map(|view_id| {
-                    self.views_metadata
-                        .get(&(handle, view_id))
-                        .map(|view| (view_id, view.keymap_context.clone()))
-                })
-                .collect();
-
-            let match_result = self
-                .keystroke_matcher
-                .push_keystroke(keystroke.clone(), dispatch_path);
-            let mut handled_by = None;
-
-            let keystroke_handled = match &match_result {
-                MatchResult::None => false,
-                MatchResult::Pending => true,
-                MatchResult::Matches(matches) => {
-                    for (view_id, action) in matches {
-                        if self.dispatch_action(Some(*view_id), action.as_ref()) {
-                            self.keystroke_matcher.clear_pending();
-                            handled_by = Some(action.boxed_clone());
-                            break;
-                        }
-                    }
-                    handled_by.is_some()
-                }
-            };
-
-            self.keystroke(handle, keystroke.clone(), handled_by, match_result.clone());
-            keystroke_handled
-        } else {
-            self.keystroke(handle, keystroke.clone(), None, MatchResult::None);
-            false
-        }
-    }
-
-    pub(crate) fn dispatch_event(&mut self, event: Event, event_reused: bool) -> bool {
-        if !event_reused {
-            self.dispatch_event_2(&event);
-        }
-
-        let mut mouse_events = SmallVec::<[_; 2]>::new();
-        let mut notified_views: HashSet<usize> = Default::default();
-        let handle = self.window_handle;
-
-        // 1. Handle platform event. Keyboard events get dispatched immediately, while mouse events
-        //    get mapped into the mouse-specific MouseEvent type.
-        //  -> These are usually small: [Mouse Down] or [Mouse up, Click] or [Mouse Moved, Mouse Dragged?]
-        //  -> Also updates mouse-related state
-        match &event {
-            Event::KeyDown(e) => return self.dispatch_key_down(e),
-
-            Event::KeyUp(e) => return self.dispatch_key_up(e),
-
-            Event::ModifiersChanged(e) => return self.dispatch_modifiers_changed(e),
-
-            Event::MouseDown(e) => {
-                // Click events are weird because they can be fired after a drag event.
-                // MDN says that browsers handle this by starting from 'the most
-                // specific ancestor element that contained both [positions]'
-                // So we need to store the overlapping regions on mouse down.
-
-                // If there is already region being clicked, don't replace it.
-                if self.window.clicked_region.is_none() {
-                    self.window.clicked_region_ids = self
-                        .window
-                        .mouse_regions
-                        .iter()
-                        .filter_map(|(region, _)| {
-                            if region.bounds.contains_point(e.position) {
-                                Some(region.id())
-                            } else {
-                                None
-                            }
-                        })
-                        .collect();
-
-                    let mut highest_z_index = 0;
-                    let mut clicked_region_id = None;
-                    for (region, z_index) in self.window.mouse_regions.iter() {
-                        if region.bounds.contains_point(e.position) && *z_index >= highest_z_index {
-                            highest_z_index = *z_index;
-                            clicked_region_id = Some(region.id());
-                        }
-                    }
-
-                    self.window.clicked_region =
-                        clicked_region_id.map(|region_id| (region_id, e.button));
-                }
-
-                mouse_events.push(MouseEvent::Down(MouseDown {
-                    region: Default::default(),
-                    platform_event: e.clone(),
-                }));
-                mouse_events.push(MouseEvent::DownOut(MouseDownOut {
-                    region: Default::default(),
-                    platform_event: e.clone(),
-                }));
-            }
-
-            Event::MouseUp(e) => {
-                mouse_events.push(MouseEvent::Up(MouseUp {
-                    region: Default::default(),
-                    platform_event: e.clone(),
-                }));
-
-                // Synthesize one last drag event to end the drag
-                mouse_events.push(MouseEvent::Drag(MouseDrag {
-                    region: Default::default(),
-                    prev_mouse_position: self.window.last_mouse_position,
-                    platform_event: MouseMovedEvent {
-                        position: e.position,
-                        pressed_button: Some(e.button),
-                        modifiers: e.modifiers,
-                    },
-                    end: true,
-                }));
-
-                mouse_events.push(MouseEvent::UpOut(MouseUpOut {
-                    region: Default::default(),
-                    platform_event: e.clone(),
-                }));
-                mouse_events.push(MouseEvent::Click(MouseClick {
-                    region: Default::default(),
-                    platform_event: e.clone(),
-                }));
-                mouse_events.push(MouseEvent::ClickOut(MouseClickOut {
-                    region: Default::default(),
-                    platform_event: e.clone(),
-                }));
-            }
-
-            Event::MouseMoved(
-                e @ MouseMovedEvent {
-                    position,
-                    pressed_button,
-                    ..
-                },
-            ) => {
-                let mut style_to_assign = CursorStyle::Arrow;
-                for region in self.window.cursor_regions.iter().rev() {
-                    if region.bounds.contains_point(*position) {
-                        style_to_assign = region.style;
-                        break;
-                    }
-                }
-
-                if pressed_button.is_none()
-                    && self
-                        .window
-                        .platform_window
-                        .is_topmost_for_position(*position)
-                {
-                    self.platform().set_cursor_style(style_to_assign);
-                }
-
-                if !event_reused {
-                    if pressed_button.is_some() {
-                        mouse_events.push(MouseEvent::Drag(MouseDrag {
-                            region: Default::default(),
-                            prev_mouse_position: self.window.last_mouse_position,
-                            platform_event: e.clone(),
-                            end: false,
-                        }));
-                    } else if let Some((_, clicked_button)) = self.window.clicked_region {
-                        mouse_events.push(MouseEvent::Drag(MouseDrag {
-                            region: Default::default(),
-                            prev_mouse_position: self.window.last_mouse_position,
-                            platform_event: e.clone(),
-                            end: true,
-                        }));
-
-                        // Mouse up event happened outside the current window. Simulate mouse up button event
-                        let button_event = e.to_button_event(clicked_button);
-                        mouse_events.push(MouseEvent::Up(MouseUp {
-                            region: Default::default(),
-                            platform_event: button_event.clone(),
-                        }));
-                        mouse_events.push(MouseEvent::UpOut(MouseUpOut {
-                            region: Default::default(),
-                            platform_event: button_event.clone(),
-                        }));
-                        mouse_events.push(MouseEvent::Click(MouseClick {
-                            region: Default::default(),
-                            platform_event: button_event.clone(),
-                        }));
-                    }
-
-                    mouse_events.push(MouseEvent::Move(MouseMove {
-                        region: Default::default(),
-                        platform_event: e.clone(),
-                    }));
-                }
-
-                mouse_events.push(MouseEvent::Hover(MouseHover {
-                    region: Default::default(),
-                    platform_event: e.clone(),
-                    started: false,
-                }));
-                mouse_events.push(MouseEvent::MoveOut(MouseMoveOut {
-                    region: Default::default(),
-                }));
-
-                self.window.last_mouse_moved_event = Some(event.clone());
-            }
-
-            Event::MouseExited(event) => {
-                // When the platform sends a MouseExited event, synthesize
-                // a MouseMoved event whose position is outside the window's
-                // bounds so that hover and cursor state can be updated.
-                return self.dispatch_event(
-                    Event::MouseMoved(MouseMovedEvent {
-                        position: event.position,
-                        pressed_button: event.pressed_button,
-                        modifiers: event.modifiers,
-                    }),
-                    event_reused,
-                );
-            }
-
-            Event::ScrollWheel(e) => mouse_events.push(MouseEvent::ScrollWheel(MouseScrollWheel {
-                region: Default::default(),
-                platform_event: e.clone(),
-            })),
-        }
-
-        if let Some(position) = event.position() {
-            self.window.last_mouse_position = position;
-        }
-
-        // 2. Dispatch mouse events on regions
-        let mut any_event_handled = false;
-        for mut mouse_event in mouse_events {
-            let mut valid_regions = Vec::new();
-
-            // GPUI elements are arranged by z_index but sibling elements can register overlapping
-            // mouse regions. As such, hover events are only fired on overlapping elements which
-            // are at the same z-index as the topmost element which overlaps with the mouse.
-            match &mouse_event {
-                MouseEvent::Hover(_) => {
-                    let mut highest_z_index = None;
-                    let mouse_position = self.mouse_position();
-                    let window = &mut *self.window;
-                    let prev_hovered_regions = mem::take(&mut window.hovered_region_ids);
-                    for (region, z_index) in window.mouse_regions.iter().rev() {
-                        // Allow mouse regions to appear transparent to hovers
-                        if !region.hoverable {
-                            continue;
-                        }
-
-                        let contains_mouse = region.bounds.contains_point(mouse_position);
-
-                        if contains_mouse && highest_z_index.is_none() {
-                            highest_z_index = Some(z_index);
-                        }
-
-                        // This unwrap relies on short circuiting boolean expressions
-                        // The right side of the && is only executed when contains_mouse
-                        // is true, and we know above that when contains_mouse is true
-                        // highest_z_index is set.
-                        if contains_mouse && z_index == highest_z_index.unwrap() {
-                            //Ensure that hover entrance events aren't sent twice
-                            if let Err(ix) = window.hovered_region_ids.binary_search(&region.id()) {
-                                window.hovered_region_ids.insert(ix, region.id());
-                            }
-                            // window.hovered_region_ids.insert(region.id());
-                            if !prev_hovered_regions.contains(&region.id()) {
-                                valid_regions.push(region.clone());
-                                if region.notify_on_hover {
-                                    notified_views.insert(region.id().view_id());
-                                }
-                            }
-                        } else {
-                            // Ensure that hover exit events aren't sent twice
-                            if prev_hovered_regions.contains(&region.id()) {
-                                valid_regions.push(region.clone());
-                                if region.notify_on_hover {
-                                    notified_views.insert(region.id().view_id());
-                                }
-                            }
-                        }
-                    }
-                }
-
-                MouseEvent::Down(_) | MouseEvent::Up(_) => {
-                    for (region, _) in self.window.mouse_regions.iter().rev() {
-                        if region.bounds.contains_point(self.mouse_position()) {
-                            valid_regions.push(region.clone());
-                            if region.notify_on_click {
-                                notified_views.insert(region.id().view_id());
-                            }
-                        }
-                    }
-                }
-
-                MouseEvent::Click(e) => {
-                    // Only raise click events if the released button is the same as the one stored
-                    if self
-                        .window
-                        .clicked_region
-                        .map(|(_, clicked_button)| clicked_button == e.button)
-                        .unwrap_or(false)
-                    {
-                        // Clear clicked regions and clicked button
-                        let clicked_region_ids = std::mem::replace(
-                            &mut self.window.clicked_region_ids,
-                            Default::default(),
-                        );
-                        self.window.clicked_region = None;
-
-                        // Find regions which still overlap with the mouse since the last MouseDown happened
-                        for (mouse_region, _) in self.window.mouse_regions.iter().rev() {
-                            if clicked_region_ids.contains(&mouse_region.id()) {
-                                if mouse_region.bounds.contains_point(self.mouse_position()) {
-                                    valid_regions.push(mouse_region.clone());
-                                } else {
-                                    // Let the view know that it hasn't been clicked anymore
-                                    if mouse_region.notify_on_click {
-                                        notified_views.insert(mouse_region.id().view_id());
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-
-                MouseEvent::Drag(_) => {
-                    for (mouse_region, _) in self.window.mouse_regions.iter().rev() {
-                        if self.window.clicked_region_ids.contains(&mouse_region.id()) {
-                            valid_regions.push(mouse_region.clone());
-                        }
-                    }
-                }
-
-                MouseEvent::MoveOut(_)
-                | MouseEvent::UpOut(_)
-                | MouseEvent::DownOut(_)
-                | MouseEvent::ClickOut(_) => {
-                    for (mouse_region, _) in self.window.mouse_regions.iter().rev() {
-                        // NOT contains
-                        if !mouse_region.bounds.contains_point(self.mouse_position()) {
-                            valid_regions.push(mouse_region.clone());
-                        }
-                    }
-                }
-
-                _ => {
-                    for (mouse_region, _) in self.window.mouse_regions.iter().rev() {
-                        // Contains
-                        if mouse_region.bounds.contains_point(self.mouse_position()) {
-                            valid_regions.push(mouse_region.clone());
-                        }
-                    }
-                }
-            }
-
-            //3. Fire region events
-            let hovered_region_ids = self.window.hovered_region_ids.clone();
-            for valid_region in valid_regions.into_iter() {
-                let mut handled = false;
-                mouse_event.set_region(valid_region.bounds);
-                if let MouseEvent::Hover(e) = &mut mouse_event {
-                    e.started = hovered_region_ids.contains(&valid_region.id())
-                }
-                // Handle Down events if the MouseRegion has a Click or Drag handler. This makes the api more intuitive as you would
-                // not expect a MouseRegion to be transparent to Down events if it also has a Click handler.
-                // This behavior can be overridden by adding a Down handler
-                if let MouseEvent::Down(e) = &mouse_event {
-                    let has_click = valid_region
-                        .handlers
-                        .contains(MouseEvent::click_disc(), Some(e.button));
-                    let has_drag = valid_region
-                        .handlers
-                        .contains(MouseEvent::drag_disc(), Some(e.button));
-                    let has_down = valid_region
-                        .handlers
-                        .contains(MouseEvent::down_disc(), Some(e.button));
-                    if !has_down && (has_click || has_drag) {
-                        handled = true;
-                    }
-                }
-
-                // `event_consumed` should only be true if there are any handlers for this event.
-                let mut event_consumed = handled;
-                if let Some(callbacks) = valid_region.handlers.get(&mouse_event.handler_key()) {
-                    for callback in callbacks {
-                        handled = true;
-                        let view_id = valid_region.id().view_id();
-                        self.update_any_view(view_id, |view, cx| {
-                            handled = callback(mouse_event.clone(), view.as_any_mut(), cx, view_id);
-                        });
-                        event_consumed |= handled;
-                        any_event_handled |= handled;
-                    }
-                }
-
-                any_event_handled |= handled;
-
-                // For bubbling events, if the event was handled, don't continue dispatching.
-                // This only makes sense for local events which return false from is_capturable.
-                if event_consumed && mouse_event.is_capturable() {
-                    break;
-                }
-            }
-        }
-
-        for view_id in notified_views {
-            self.notify_view(handle, view_id);
-        }
-
-        any_event_handled
-    }
-
-    fn dispatch_event_2(&mut self, event: &Event) {
-        match event {
-            Event::MouseDown(event) => {
-                self.window.pressed_buttons.insert(event.button);
-            }
-            Event::MouseUp(event) => {
-                self.window.pressed_buttons.remove(&event.button);
-            }
-            _ => {}
-        }
-
-        if let Some(mouse_event) = event.mouse_event() {
-            let event_handlers = self.window.take_event_handlers();
-            for event_handler in event_handlers.iter().rev() {
-                if event_handler.event_type == mouse_event.type_id() {
-                    if !(event_handler.handler)(mouse_event, self) {
-                        break;
-                    }
-                }
-            }
-            self.window.event_handlers = event_handlers;
-        }
-    }
-
-    pub(crate) fn dispatch_key_down(&mut self, event: &KeyDownEvent) -> bool {
-        let handle = self.window_handle;
-        if let Some(focused_view_id) = self.window.focused_view_id {
-            for view_id in self.ancestors(focused_view_id).collect::<Vec<_>>() {
-                if let Some(mut view) = self.views.remove(&(handle, view_id)) {
-                    let handled = view.key_down(event, self, view_id);
-                    self.views.insert((handle, view_id), view);
-                    if handled {
-                        return true;
-                    }
-                } else {
-                    log::error!("view {} does not exist", view_id)
-                }
-            }
-        }
-
-        false
-    }
-
-    pub(crate) fn dispatch_key_up(&mut self, event: &KeyUpEvent) -> bool {
-        let handle = self.window_handle;
-        if let Some(focused_view_id) = self.window.focused_view_id {
-            for view_id in self.ancestors(focused_view_id).collect::<Vec<_>>() {
-                if let Some(mut view) = self.views.remove(&(handle, view_id)) {
-                    let handled = view.key_up(event, self, view_id);
-                    self.views.insert((handle, view_id), view);
-                    if handled {
-                        return true;
-                    }
-                } else {
-                    log::error!("view {} does not exist", view_id)
-                }
-            }
-        }
-
-        false
-    }
-
-    pub(crate) fn dispatch_modifiers_changed(&mut self, event: &ModifiersChangedEvent) -> bool {
-        let handle = self.window_handle;
-        if let Some(focused_view_id) = self.window.focused_view_id {
-            for view_id in self.ancestors(focused_view_id).collect::<Vec<_>>() {
-                if let Some(mut view) = self.views.remove(&(handle, view_id)) {
-                    let handled = view.modifiers_changed(event, self, view_id);
-                    self.views.insert((handle, view_id), view);
-                    if handled {
-                        return true;
-                    }
-                } else {
-                    log::error!("view {} does not exist", view_id)
-                }
-            }
-        }
-
-        false
-    }
-
-    pub fn invalidate(&mut self, mut invalidation: WindowInvalidation, appearance: Appearance) {
-        self.start_frame();
-        self.window.appearance = appearance;
-        for view_id in &invalidation.removed {
-            invalidation.updated.remove(view_id);
-            self.window.rendered_views.remove(view_id);
-        }
-        for view_id in &invalidation.updated {
-            let titlebar_height = self.window.titlebar_height;
-            let element = self
-                .render_view(RenderParams {
-                    view_id: *view_id,
-                    titlebar_height,
-                    refreshing: false,
-                    appearance,
-                })
-                .unwrap();
-            self.window.rendered_views.insert(*view_id, element);
-        }
-    }
-
-    pub fn render_view(&mut self, params: RenderParams) -> Result<Box<dyn AnyRootElement>> {
-        let handle = self.window_handle;
-        let view_id = params.view_id;
-        let mut view = self
-            .views
-            .remove(&(handle, view_id))
-            .ok_or_else(|| anyhow!("view not found"))?;
-        let element = view.render(self, view_id);
-        self.views.insert((handle, view_id), view);
-        Ok(element)
-    }
-
-    pub fn layout(&mut self, refreshing: bool) -> Result<HashMap<usize, usize>> {
-        let window_size = self.window.platform_window.content_size();
-        let root_view_id = self.window.root_view().id();
-
-        let mut rendered_root = self.window.rendered_views.remove(&root_view_id).unwrap();
-
-        self.window.refreshing = refreshing;
-        rendered_root.layout(SizeConstraint::strict(window_size), self)?;
-        self.window.refreshing = false;
-
-        let views_to_notify_if_ancestors_change =
-            mem::take(&mut self.window.views_to_notify_if_ancestors_change);
-        for (view_id, view_ids_to_notify) in views_to_notify_if_ancestors_change {
-            let mut current_view_id = view_id;
-            loop {
-                let old_parent_id = self.window.parents.get(&current_view_id);
-                let new_parent_id = self.window.new_parents.get(&current_view_id);
-                if old_parent_id.is_none() && new_parent_id.is_none() {
-                    break;
-                } else if old_parent_id == new_parent_id {
-                    current_view_id = *old_parent_id.unwrap();
-                } else {
-                    let handle = self.window_handle;
-                    for view_id_to_notify in view_ids_to_notify {
-                        self.notify_view(handle, view_id_to_notify);
-                    }
-                    break;
-                }
-            }
-        }
-
-        let new_parents = mem::take(&mut self.window.new_parents);
-        let old_parents = mem::replace(&mut self.window.parents, new_parents);
-        self.window
-            .rendered_views
-            .insert(root_view_id, rendered_root);
-        Ok(old_parents)
-    }
-
-    pub fn paint(&mut self) -> Result<Scene> {
-        let window_size = self.window.platform_window.content_size();
-        let scale_factor = self.window.platform_window.scale_factor();
-
-        let root_view_id = self.window.root_view().id();
-        let mut rendered_root = self.window.rendered_views.remove(&root_view_id).unwrap();
-
-        rendered_root.paint(
-            Vector2F::zero(),
-            RectF::from_points(Vector2F::zero(), window_size),
-            self,
-        )?;
-        self.window
-            .rendered_views
-            .insert(root_view_id, rendered_root);
-
-        self.window.text_layout_cache.finish_frame();
-        let mut scene = self.window.scene.build(scale_factor);
-        self.window.cursor_regions = scene.cursor_regions();
-        self.window.mouse_regions = scene.mouse_regions();
-        self.window.event_handlers = scene.take_event_handlers();
-
-        if self.window_is_active() {
-            if let Some(event) = self.window.last_mouse_moved_event.clone() {
-                self.dispatch_event(event, true);
-            }
-        }
-
-        Ok(scene)
-    }
-
-    pub fn root_element(&self) -> &Box<dyn AnyRootElement> {
-        let view_id = self.window.root_view().id();
-        self.window.rendered_views.get(&view_id).unwrap()
-    }
-
-    pub fn rect_for_text_range(&self, range_utf16: Range<usize>) -> Option<RectF> {
-        let focused_view_id = self.window.focused_view_id?;
-        self.window
-            .rendered_views
-            .get(&focused_view_id)?
-            .rect_for_text_range(range_utf16, self)
-            .log_err()
-            .flatten()
-    }
-
-    pub fn set_window_title(&mut self, title: &str) {
-        self.window.platform_window.set_title(title);
-    }
-
-    pub fn set_window_edited(&mut self, edited: bool) {
-        self.window.platform_window.set_edited(edited);
-    }
-
-    pub fn is_topmost_window_for_position(&self, position: Vector2F) -> bool {
-        self.window
-            .platform_window
-            .is_topmost_for_position(position)
-    }
-
-    pub fn activate_window(&self) {
-        self.window.platform_window.activate();
-    }
-
-    pub fn window_is_active(&self) -> bool {
-        self.window.is_active
-    }
-
-    pub fn window_is_fullscreen(&self) -> bool {
-        self.window.is_fullscreen
-    }
-
-    pub fn dispatch_action(&mut self, view_id: Option<usize>, action: &dyn Action) -> bool {
-        if let Some(view_id) = view_id {
-            self.halt_action_dispatch = false;
-            self.visit_dispatch_path(view_id, |view_id, capture_phase, cx| {
-                cx.update_any_view(view_id, |view, cx| {
-                    let type_id = view.as_any().type_id();
-                    if let Some((name, mut handlers)) = cx
-                        .actions_mut(capture_phase)
-                        .get_mut(&type_id)
-                        .and_then(|h| h.remove_entry(&action.id()))
-                    {
-                        for handler in handlers.iter_mut().rev() {
-                            cx.halt_action_dispatch = true;
-                            handler(view, action, cx, view_id);
-                            if cx.halt_action_dispatch {
-                                break;
-                            }
-                        }
-                        cx.actions_mut(capture_phase)
-                            .get_mut(&type_id)
-                            .unwrap()
-                            .insert(name, handlers);
-                    }
-                });
-
-                !cx.halt_action_dispatch
-            });
-        }
-
-        if !self.halt_action_dispatch {
-            self.halt_action_dispatch = self.dispatch_global_action_any(action);
-        }
-
-        self.pending_effects
-            .push_back(Effect::ActionDispatchNotification {
-                action_id: action.id(),
-            });
-        self.halt_action_dispatch
-    }
-
-    /// Returns an iterator over all of the view ids from the passed view up to the root of the window
-    /// Includes the passed view itself
-    pub(crate) fn ancestors(&self, mut view_id: usize) -> impl Iterator<Item = usize> + '_ {
-        std::iter::once(view_id)
-            .into_iter()
-            .chain(std::iter::from_fn(move || {
-                if let Some(parent_id) = self.window.parents.get(&view_id) {
-                    view_id = *parent_id;
-                    Some(view_id)
-                } else {
-                    None
-                }
-            }))
-    }
-
-    // Traverses the parent tree. Walks down the tree toward the passed
-    // view calling visit with true. Then walks back up the tree calling visit with false.
-    // If `visit` returns false this function will immediately return.
-    fn visit_dispatch_path(
-        &mut self,
-        view_id: usize,
-        mut visit: impl FnMut(usize, bool, &mut WindowContext) -> bool,
-    ) {
-        // List of view ids from the leaf to the root of the window
-        let path = self.ancestors(view_id).collect::<Vec<_>>();
-
-        // Walk down from the root to the leaf calling visit with capture_phase = true
-        for view_id in path.iter().rev() {
-            if !visit(*view_id, true, self) {
-                return;
-            }
-        }
-
-        // Walk up from the leaf to the root calling visit with capture_phase = false
-        for view_id in path.iter() {
-            if !visit(*view_id, false, self) {
-                return;
-            }
-        }
-    }
-
-    pub fn focused_view_id(&self) -> Option<usize> {
-        self.window.focused_view_id
-    }
-
-    pub fn focus(&mut self, view_id: Option<usize>) {
-        self.app_context.focus(self.window_handle, view_id);
-    }
-
-    pub fn window_bounds(&self) -> WindowBounds {
-        self.window.platform_window.bounds()
-    }
-
-    pub fn titlebar_height(&self) -> f32 {
-        self.window.titlebar_height
-    }
-
-    pub fn window_appearance(&self) -> Appearance {
-        self.window.appearance
-    }
-
-    pub fn window_display_uuid(&self) -> Option<Uuid> {
-        self.window.platform_window.screen().display_uuid()
-    }
-
-    pub fn show_character_palette(&self) {
-        self.window.platform_window.show_character_palette();
-    }
-
-    pub fn minimize_window(&self) {
-        self.window.platform_window.minimize();
-    }
-
-    pub fn zoom_window(&self) {
-        self.window.platform_window.zoom();
-    }
-
-    pub fn toggle_full_screen(&self) {
-        self.window.platform_window.toggle_full_screen();
-    }
-
-    pub fn prompt(
-        &self,
-        level: PromptLevel,
-        msg: &str,
-        answers: &[&str],
-    ) -> oneshot::Receiver<usize> {
-        self.window.platform_window.prompt(level, msg, answers)
-    }
-
-    pub fn add_view<T, F>(&mut self, build_view: F) -> ViewHandle<T>
-    where
-        T: View,
-        F: FnOnce(&mut ViewContext<T>) -> T,
-    {
-        self.add_option_view(|cx| Some(build_view(cx))).unwrap()
-    }
-
-    pub fn add_option_view<T, F>(&mut self, build_view: F) -> Option<ViewHandle<T>>
-    where
-        T: View,
-        F: FnOnce(&mut ViewContext<T>) -> Option<T>,
-    {
-        let handle = self.window_handle;
-        let view_id = post_inc(&mut self.next_id);
-        let mut cx = ViewContext::mutable(self, view_id);
-        let handle = if let Some(view) = build_view(&mut cx) {
-            let mut keymap_context = KeymapContext::default();
-            view.update_keymap_context(&mut keymap_context, cx.app_context());
-            self.views_metadata.insert(
-                (handle, view_id),
-                ViewMetadata {
-                    type_id: TypeId::of::<T>(),
-                    keymap_context,
-                },
-            );
-            self.views.insert((handle, view_id), Box::new(view));
-            self.window
-                .invalidation
-                .get_or_insert_with(Default::default)
-                .updated
-                .insert(view_id);
-            Some(ViewHandle::new(handle, view_id, &self.ref_counts))
-        } else {
-            None
-        };
-        handle
-    }
-
-    pub fn text_style(&self) -> TextStyle {
-        self.window
-            .text_style_stack
-            .last()
-            .cloned()
-            .unwrap_or(TextStyle::default(&self.font_cache))
-    }
-
-    pub fn push_text_style(&mut self, refinement: &TextStyleRefinement) -> Result<()> {
-        let mut style = self.text_style();
-        style.refine(refinement, self.font_cache())?;
-        self.window.text_style_stack.push(style);
-        Ok(())
-    }
-
-    pub fn pop_text_style(&mut self) {
-        self.window.text_style_stack.pop();
-    }
-
-    pub fn theme<T: 'static + Send + Sync>(&self) -> Arc<T> {
-        self.window
-            .theme_stack
-            .iter()
-            .rev()
-            .find_map(|theme| {
-                let entry = Arc::clone(theme);
-                entry.downcast::<T>().ok()
-            })
-            .ok_or_else(|| anyhow!("no theme provided of type {}", type_name::<T>()))
-            .unwrap()
-    }
-
-    pub fn push_theme<T: 'static + Send + Sync>(&mut self, theme: T) {
-        self.window.theme_stack.push(Arc::new(theme));
-    }
-
-    pub fn pop_theme(&mut self) {
-        self.window.theme_stack.pop();
-    }
-}
-
-#[derive(Default)]
-pub struct LayoutEngine(Taffy);
-pub use taffy::style::Style as LayoutStyle;
-
-impl LayoutEngine {
-    pub fn new() -> Self {
-        Default::default()
-    }
-
-    pub fn add_node<C>(&mut self, style: LayoutStyle, children: C) -> Result<LayoutId>
-    where
-        C: IntoIterator<Item = LayoutId>,
-    {
-        let children = children.into_iter().collect::<Vec<_>>();
-        if children.is_empty() {
-            Ok(self.0.new_leaf(style)?)
-        } else {
-            Ok(self.0.new_with_children(style, &children)?)
-        }
-    }
-
-    pub fn add_measured_node<F>(&mut self, style: LayoutStyle, measure: F) -> Result<LayoutId>
-    where
-        F: Fn(MeasureParams) -> Size<f32> + Sync + Send + 'static,
-    {
-        Ok(self
-            .0
-            .new_leaf_with_measure(style, MeasureFunc::Boxed(Box::new(MeasureFn(measure))))?)
-    }
-
-    pub fn compute_layout(&mut self, root: LayoutId, available_space: Vector2F) -> Result<()> {
-        self.0.compute_layout(
-            root,
-            taffy::geometry::Size {
-                width: available_space.x().into(),
-                height: available_space.y().into(),
-            },
-        )?;
-        Ok(())
-    }
-
-    pub fn computed_layout(&mut self, node: LayoutId) -> Result<Layout> {
-        Ok(Layout::from(self.0.layout(node)?))
-    }
-}
-
-pub struct MeasureFn<F>(F);
-
-impl<F: Send + Sync> Measurable for MeasureFn<F>
-where
-    F: Fn(MeasureParams) -> Size<f32>,
-{
-    fn measure(
-        &self,
-        known_dimensions: taffy::prelude::Size<Option<f32>>,
-        available_space: taffy::prelude::Size<taffy::style::AvailableSpace>,
-    ) -> taffy::prelude::Size<f32> {
-        (self.0)(MeasureParams {
-            known_dimensions: known_dimensions.into(),
-            available_space: available_space.into(),
-        })
-        .into()
-    }
-}
-
-#[derive(Debug, Clone, Default)]
-pub struct Layout {
-    pub bounds: RectF,
-    pub order: u32,
-}
-
-pub struct MeasureParams {
-    pub known_dimensions: Size<Option<f32>>,
-    pub available_space: Size<AvailableSpace>,
-}
-
-#[derive(Clone, Debug)]
-pub enum AvailableSpace {
-    /// The amount of space available is the specified number of pixels
-    Pixels(f32),
-    /// The amount of space available is indefinite and the node should be laid out under a min-content constraint
-    MinContent,
-    /// The amount of space available is indefinite and the node should be laid out under a max-content constraint
-    MaxContent,
-}
-
-impl Default for AvailableSpace {
-    fn default() -> Self {
-        Self::Pixels(0.)
-    }
-}
-
-impl From<taffy::prelude::AvailableSpace> for AvailableSpace {
-    fn from(value: taffy::prelude::AvailableSpace) -> Self {
-        match value {
-            taffy::prelude::AvailableSpace::Definite(pixels) => Self::Pixels(pixels),
-            taffy::prelude::AvailableSpace::MinContent => Self::MinContent,
-            taffy::prelude::AvailableSpace::MaxContent => Self::MaxContent,
-        }
-    }
-}
-
-impl From<&taffy::tree::Layout> for Layout {
-    fn from(value: &taffy::tree::Layout) -> Self {
-        Self {
-            bounds: RectF::new(
-                vec2f(value.location.x, value.location.y),
-                vec2f(value.size.width, value.size.height),
-            ),
-            order: value.order,
-        }
-    }
-}
-
-pub type LayoutId = taffy::prelude::NodeId;
-
-pub struct RenderParams {
-    pub view_id: usize,
-    pub titlebar_height: f32,
-    pub refreshing: bool,
-    pub appearance: Appearance,
-}
-
-#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
-pub enum Axis {
-    #[default]
-    Horizontal,
-    Vertical,
-}
-
-impl Axis {
-    pub fn invert(self) -> Self {
-        match self {
-            Self::Horizontal => Self::Vertical,
-            Self::Vertical => Self::Horizontal,
-        }
-    }
-
-    pub fn component(&self, point: Vector2F) -> f32 {
-        match self {
-            Self::Horizontal => point.x(),
-            Self::Vertical => point.y(),
-        }
-    }
-}
-
-impl ToJson for Axis {
-    fn to_json(&self) -> serde_json::Value {
-        match self {
-            Axis::Horizontal => json!("horizontal"),
-            Axis::Vertical => json!("vertical"),
-        }
-    }
-}
-
-impl StaticColumnCount for Axis {}
-impl Bind for Axis {
-    fn bind(&self, statement: &Statement, start_index: i32) -> anyhow::Result<i32> {
-        match self {
-            Axis::Horizontal => "Horizontal",
-            Axis::Vertical => "Vertical",
-        }
-        .bind(statement, start_index)
-    }
-}
-
-impl Column for Axis {
-    fn column(statement: &mut Statement, start_index: i32) -> anyhow::Result<(Self, i32)> {
-        String::column(statement, start_index).and_then(|(axis_text, next_index)| {
-            Ok((
-                match axis_text.as_str() {
-                    "Horizontal" => Axis::Horizontal,
-                    "Vertical" => Axis::Vertical,
-                    _ => bail!("Stored serialized item kind is incorrect"),
-                },
-                next_index,
-            ))
-        })
-    }
-}
-
-pub trait Vector2FExt {
-    fn along(self, axis: Axis) -> f32;
-}
-
-impl Vector2FExt for Vector2F {
-    fn along(self, axis: Axis) -> f32 {
-        match axis {
-            Axis::Horizontal => self.x(),
-            Axis::Vertical => self.y(),
-        }
-    }
-}
-
-pub trait RectFExt {
-    fn length_along(self, axis: Axis) -> f32;
-}
-
-impl RectFExt for RectF {
-    fn length_along(self, axis: Axis) -> f32 {
-        match axis {
-            Axis::Horizontal => self.width(),
-            Axis::Vertical => self.height(),
-        }
-    }
-}
-
-#[derive(Copy, Clone, Debug)]
-pub struct SizeConstraint {
-    pub min: Vector2F,
-    pub max: Vector2F,
-}
-
-impl SizeConstraint {
-    pub fn new(min: Vector2F, max: Vector2F) -> Self {
-        Self { min, max }
-    }
-
-    pub fn strict(size: Vector2F) -> Self {
-        Self {
-            min: size,
-            max: size,
-        }
-    }
-    pub fn loose(max: Vector2F) -> Self {
-        Self {
-            min: Vector2F::zero(),
-            max,
-        }
-    }
-
-    pub fn strict_along(axis: Axis, max: f32) -> Self {
-        match axis {
-            Axis::Horizontal => Self {
-                min: vec2f(max, 0.0),
-                max: vec2f(max, f32::INFINITY),
-            },
-            Axis::Vertical => Self {
-                min: vec2f(0.0, max),
-                max: vec2f(f32::INFINITY, max),
-            },
-        }
-    }
-
-    pub fn max_along(&self, axis: Axis) -> f32 {
-        match axis {
-            Axis::Horizontal => self.max.x(),
-            Axis::Vertical => self.max.y(),
-        }
-    }
-
-    pub fn min_along(&self, axis: Axis) -> f32 {
-        match axis {
-            Axis::Horizontal => self.min.x(),
-            Axis::Vertical => self.min.y(),
-        }
-    }
-
-    pub fn constrain(&self, size: Vector2F) -> Vector2F {
-        vec2f(
-            size.x().min(self.max.x()).max(self.min.x()),
-            size.y().min(self.max.y()).max(self.min.y()),
-        )
-    }
-}
-
-impl Sub<Vector2F> for SizeConstraint {
-    type Output = SizeConstraint;
-
-    fn sub(self, rhs: Vector2F) -> SizeConstraint {
-        SizeConstraint {
-            min: self.min - rhs,
-            max: self.max - rhs,
-        }
-    }
-}
-
-impl Default for SizeConstraint {
-    fn default() -> Self {
-        SizeConstraint {
-            min: Vector2F::zero(),
-            max: Vector2F::splat(f32::INFINITY),
-        }
-    }
-}
-
-impl ToJson for SizeConstraint {
-    fn to_json(&self) -> serde_json::Value {
-        json!({
-            "min": self.min.to_json(),
-            "max": self.max.to_json(),
-        })
-    }
-}
-
-#[derive(Clone)]
-pub struct ChildView {
-    view_id: usize,
-    view_name: &'static str,
-}
-
-impl ChildView {
-    pub fn new(view: &AnyViewHandle, cx: &AppContext) -> Self {
-        let view_name = cx.view_ui_name(view.window, view.id()).unwrap();
-        Self {
-            view_id: view.id(),
-            view_name,
-        }
-    }
-}
-
-impl<V: 'static> Element<V> for ChildView {
-    type LayoutState = ();
-    type PaintState = ();
-
-    fn layout(
-        &mut self,
-        constraint: SizeConstraint,
-        _: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> (Vector2F, Self::LayoutState) {
-        if let Some(mut rendered_view) = cx.window.rendered_views.remove(&self.view_id) {
-            let parent_id = cx.view_id();
-            cx.window.new_parents.insert(self.view_id, parent_id);
-            let size = rendered_view
-                .layout(constraint, cx)
-                .log_err()
-                .unwrap_or(Vector2F::zero());
-            cx.window.rendered_views.insert(self.view_id, rendered_view);
-            (size, ())
-        } else {
-            log::error!(
-                "layout called on a ChildView element whose underlying view was dropped (view_id: {}, name: {:?})",
-                self.view_id,
-                self.view_name
-            );
-            (Vector2F::zero(), ())
-        }
-    }
-
-    fn paint(
-        &mut self,
-        bounds: RectF,
-        visible_bounds: RectF,
-        _: &mut Self::LayoutState,
-        _: &mut V,
-        cx: &mut ViewContext<V>,
-    ) {
-        if let Some(mut rendered_view) = cx.window.rendered_views.remove(&self.view_id) {
-            rendered_view
-                .paint(bounds.origin(), visible_bounds, cx)
-                .log_err();
-            cx.window.rendered_views.insert(self.view_id, rendered_view);
-        } else {
-            log::error!(
-                "paint called on a ChildView element whose underlying view was dropped (view_id: {}, name: {:?})",
-                self.view_id,
-                self.view_name
-            );
-        }
-    }
-
-    fn rect_for_text_range(
-        &self,
-        range_utf16: Range<usize>,
-        _: RectF,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        _: &V,
-        cx: &ViewContext<V>,
-    ) -> Option<RectF> {
-        if let Some(rendered_view) = cx.window.rendered_views.get(&self.view_id) {
-            rendered_view
-                .rect_for_text_range(range_utf16, &cx.window_context)
-                .log_err()
-                .flatten()
-        } else {
-            log::error!(
-                "rect_for_text_range called on a ChildView element whose underlying view was dropped (view_id: {}, name: {:?})",
-                self.view_id,
-                self.view_name
-            );
-            None
-        }
-    }
-
-    fn debug(
-        &self,
-        bounds: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        _: &V,
-        cx: &ViewContext<V>,
-    ) -> serde_json::Value {
-        json!({
-            "type": "ChildView",
-            "bounds": bounds.to_json(),
-            "child": if let Some(element) = cx.window.rendered_views.get(&self.view_id) {
-                element.debug(&cx.window_context).log_err().unwrap_or_else(|| json!(null))
-            } else {
-                json!(null)
-            }
-        })
-    }
-}

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

@@ -1,90 +0,0 @@
-use std::{cell::RefCell, ops::Range, rc::Rc};
-
-use pathfinder_geometry::rect::RectF;
-
-use crate::{platform::InputHandler, window::WindowContext, AnyView, AnyWindowHandle, AppContext};
-
-pub struct WindowInputHandler {
-    pub app: Rc<RefCell<AppContext>>,
-    pub window: AnyWindowHandle,
-}
-
-impl WindowInputHandler {
-    fn read_focused_view<T, F>(&self, f: F) -> Option<T>
-    where
-        F: FnOnce(&dyn AnyView, &WindowContext) -> T,
-    {
-        // Input-related application hooks are sometimes called by the OS during
-        // a call to a window-manipulation API, like prompting the user for file
-        // paths. In that case, the AppContext will already be borrowed, so any
-        // InputHandler methods need to fail gracefully.
-        //
-        // See https://github.com/zed-industries/community/issues/444
-        let mut app = self.app.try_borrow_mut().ok()?;
-        self.window.update_optional(&mut *app, |cx| {
-            let view_id = cx.window.focused_view_id?;
-            let view = cx.views.get(&(self.window, view_id))?;
-            let result = f(view.as_ref(), &cx);
-            Some(result)
-        })
-    }
-
-    fn update_focused_view<T, F>(&mut self, f: F) -> Option<T>
-    where
-        F: FnOnce(&mut dyn AnyView, &mut WindowContext, usize) -> T,
-    {
-        let mut app = self.app.try_borrow_mut().ok()?;
-        self.window
-            .update(&mut *app, |cx| {
-                let view_id = cx.window.focused_view_id?;
-                cx.update_any_view(view_id, |view, cx| f(view, cx, view_id))
-            })
-            .flatten()
-    }
-}
-
-impl InputHandler for WindowInputHandler {
-    fn text_for_range(&self, range: Range<usize>) -> Option<String> {
-        self.read_focused_view(|view, cx| view.text_for_range(range.clone(), cx))
-            .flatten()
-    }
-
-    fn selected_text_range(&self) -> Option<Range<usize>> {
-        self.read_focused_view(|view, cx| view.selected_text_range(cx))
-            .flatten()
-    }
-
-    fn replace_text_in_range(&mut self, range: Option<Range<usize>>, text: &str) {
-        self.update_focused_view(|view, cx, view_id| {
-            view.replace_text_in_range(range, text, cx, view_id);
-        });
-    }
-
-    fn marked_text_range(&self) -> Option<Range<usize>> {
-        self.read_focused_view(|view, cx| view.marked_text_range(cx))
-            .flatten()
-    }
-
-    fn unmark_text(&mut self) {
-        self.update_focused_view(|view, cx, view_id| {
-            view.unmark_text(cx, view_id);
-        });
-    }
-
-    fn replace_and_mark_text_in_range(
-        &mut self,
-        range: Option<Range<usize>>,
-        new_text: &str,
-        new_selected_range: Option<Range<usize>>,
-    ) {
-        self.update_focused_view(|view, cx, view_id| {
-            view.replace_and_mark_text_in_range(range, new_text, new_selected_range, cx, view_id);
-        });
-    }
-
-    fn rect_for_range(&self, range_utf16: Range<usize>) -> Option<RectF> {
-        self.window.read_optional_with(&*self.app.borrow(), |cx| {
-            cx.rect_for_text_range(range_utf16)
-        })
-    }
-}

crates/gpui/src/assets.rs 🔗

@@ -1,12 +1,16 @@
-use anyhow::{anyhow, Result};
-use image::ImageFormat;
-use std::{borrow::Cow, cell::RefCell, collections::HashMap, sync::Arc};
-
-use crate::ImageData;
+use crate::{size, DevicePixels, Result, SharedString, Size};
+use anyhow::anyhow;
+use image::{Bgra, ImageBuffer};
+use std::{
+    borrow::Cow,
+    fmt,
+    hash::Hash,
+    sync::atomic::{AtomicUsize, Ordering::SeqCst},
+};
 
 pub trait AssetSource: 'static + Send + Sync {
     fn load(&self, path: &str) -> Result<Cow<[u8]>>;
-    fn list(&self, path: &str) -> Vec<Cow<'static, str>>;
+    fn list(&self, path: &str) -> Result<Vec<SharedString>>;
 }
 
 impl AssetSource for () {
@@ -17,49 +21,44 @@ impl AssetSource for () {
         ))
     }
 
-    fn list(&self, _: &str) -> Vec<Cow<'static, str>> {
-        vec![]
+    fn list(&self, _path: &str) -> Result<Vec<SharedString>> {
+        Ok(vec![])
     }
 }
 
-pub struct AssetCache {
-    source: Box<dyn AssetSource>,
-    svgs: RefCell<HashMap<String, usvg::Tree>>,
-    pngs: RefCell<HashMap<String, Arc<ImageData>>>,
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub struct ImageId(usize);
+
+pub struct ImageData {
+    pub id: ImageId,
+    data: ImageBuffer<Bgra<u8>, Vec<u8>>,
 }
 
-impl AssetCache {
-    pub fn new(source: impl AssetSource) -> Self {
+impl ImageData {
+    pub fn new(data: ImageBuffer<Bgra<u8>, Vec<u8>>) -> Self {
+        static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
+
         Self {
-            source: Box::new(source),
-            svgs: RefCell::new(HashMap::new()),
-            pngs: RefCell::new(HashMap::new()),
+            id: ImageId(NEXT_ID.fetch_add(1, SeqCst)),
+            data,
         }
     }
 
-    pub fn svg(&self, path: &str) -> Result<usvg::Tree> {
-        let mut svgs = self.svgs.borrow_mut();
-        if let Some(svg) = svgs.get(path) {
-            Ok(svg.clone())
-        } else {
-            let bytes = self.source.load(path)?;
-            let svg = usvg::Tree::from_data(&bytes, &usvg::Options::default())?;
-            svgs.insert(path.to_string(), svg.clone());
-            Ok(svg)
-        }
+    pub fn as_bytes(&self) -> &[u8] {
+        &self.data
     }
 
-    pub fn png(&self, path: &str) -> Result<Arc<ImageData>> {
-        let mut pngs = self.pngs.borrow_mut();
-        if let Some(png) = pngs.get(path) {
-            Ok(png.clone())
-        } else {
-            let bytes = self.source.load(path)?;
-            let image = ImageData::new(
-                image::load_from_memory_with_format(&bytes, ImageFormat::Png)?.into_bgra8(),
-            );
-            pngs.insert(path.to_string(), image.clone());
-            Ok(image)
-        }
+    pub fn size(&self) -> Size<DevicePixels> {
+        let (width, height) = self.data.dimensions();
+        size(width.into(), height.into())
+    }
+}
+
+impl fmt::Debug for ImageData {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("ImageData")
+            .field("id", &self.id)
+            .field("size", &self.data.dimensions())
+            .finish()
     }
 }

crates/gpui/src/clipboard.rs 🔗

@@ -1,42 +0,0 @@
-use seahash::SeaHasher;
-use serde::{Deserialize, Serialize};
-use std::hash::{Hash, Hasher};
-
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct ClipboardItem {
-    pub(crate) text: String,
-    pub(crate) metadata: Option<String>,
-}
-
-impl ClipboardItem {
-    pub fn new(text: String) -> Self {
-        Self {
-            text,
-            metadata: None,
-        }
-    }
-
-    pub fn with_metadata<T: Serialize>(mut self, metadata: T) -> Self {
-        self.metadata = Some(serde_json::to_string(&metadata).unwrap());
-        self
-    }
-
-    pub fn text(&self) -> &String {
-        &self.text
-    }
-
-    pub fn metadata<T>(&self) -> Option<T>
-    where
-        T: for<'a> Deserialize<'a>,
-    {
-        self.metadata
-            .as_ref()
-            .and_then(|m| serde_json::from_str(m).ok())
-    }
-
-    pub(crate) fn text_hash(text: &str) -> u64 {
-        let mut hasher = SeaHasher::new();
-        text.hash(&mut hasher);
-        hasher.finish()
-    }
-}

crates/gpui/src/color.rs 🔗

@@ -1,65 +1,223 @@
-use std::{
-    borrow::Cow,
-    fmt,
-    ops::{Deref, DerefMut},
-};
+use anyhow::bail;
+use serde::de::{self, Deserialize, Deserializer, Visitor};
+use std::fmt;
+
+pub fn rgb<C: From<Rgba>>(hex: u32) -> C {
+    let r = ((hex >> 16) & 0xFF) as f32 / 255.0;
+    let g = ((hex >> 8) & 0xFF) as f32 / 255.0;
+    let b = (hex & 0xFF) as f32 / 255.0;
+    Rgba { r, g, b, a: 1.0 }.into()
+}
 
-use crate::json::ToJson;
-use pathfinder_color::{ColorF, ColorU};
-use schemars::JsonSchema;
-use serde::{
-    de::{self, Unexpected},
-    Deserialize, Deserializer,
-};
-use serde_json::json;
+pub fn rgba(hex: u32) -> Rgba {
+    let r = ((hex >> 24) & 0xFF) as f32 / 255.0;
+    let g = ((hex >> 16) & 0xFF) as f32 / 255.0;
+    let b = ((hex >> 8) & 0xFF) as f32 / 255.0;
+    let a = (hex & 0xFF) as f32 / 255.0;
+    Rgba { r, g, b, a }
+}
 
-#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord, JsonSchema)]
-#[repr(transparent)]
-pub struct Color(#[schemars(with = "String")] pub ColorU);
+#[derive(PartialEq, Clone, Copy, Default)]
+pub struct Rgba {
+    pub r: f32,
+    pub g: f32,
+    pub b: f32,
+    pub a: f32,
+}
+
+impl fmt::Debug for Rgba {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "rgba({:#010x})", u32::from(*self))
+    }
+}
 
-pub fn color(rgba: u32) -> Color {
-    Color::from_u32(rgba)
+impl Rgba {
+    pub fn blend(&self, other: Rgba) -> Self {
+        if other.a >= 1.0 {
+            other
+        } else if other.a <= 0.0 {
+            return *self;
+        } else {
+            return Rgba {
+                r: (self.r * (1.0 - other.a)) + (other.r * other.a),
+                g: (self.g * (1.0 - other.a)) + (other.g * other.a),
+                b: (self.b * (1.0 - other.a)) + (other.b * other.a),
+                a: self.a,
+            };
+        }
+    }
 }
 
-pub fn rgb(r: f32, g: f32, b: f32) -> Color {
-    Color(ColorF::new(r, g, b, 1.).to_u8())
+impl From<Rgba> for u32 {
+    fn from(rgba: Rgba) -> Self {
+        let r = (rgba.r * 255.0) as u32;
+        let g = (rgba.g * 255.0) as u32;
+        let b = (rgba.b * 255.0) as u32;
+        let a = (rgba.a * 255.0) as u32;
+        (r << 24) | (g << 16) | (b << 8) | a
+    }
 }
 
-pub fn rgba(r: f32, g: f32, b: f32, a: f32) -> Color {
-    Color(ColorF::new(r, g, b, a).to_u8())
+struct RgbaVisitor;
+
+impl<'de> Visitor<'de> for RgbaVisitor {
+    type Value = Rgba;
+
+    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        formatter.write_str("a string in the format #rrggbb or #rrggbbaa")
+    }
+
+    fn visit_str<E: de::Error>(self, value: &str) -> Result<Rgba, E> {
+        Rgba::try_from(value).map_err(E::custom)
+    }
 }
 
-pub fn transparent_black() -> Color {
-    Color(ColorU::transparent_black())
+impl<'de> Deserialize<'de> for Rgba {
+    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+        deserializer.deserialize_str(RgbaVisitor)
+    }
 }
 
-pub fn black() -> Color {
-    Color(ColorU::black())
+impl From<Hsla> for Rgba {
+    fn from(color: Hsla) -> Self {
+        let h = color.h;
+        let s = color.s;
+        let l = color.l;
+
+        let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
+        let x = c * (1.0 - ((h * 6.0) % 2.0 - 1.0).abs());
+        let m = l - c / 2.0;
+        let cm = c + m;
+        let xm = x + m;
+
+        let (r, g, b) = match (h * 6.0).floor() as i32 {
+            0 | 6 => (cm, xm, m),
+            1 => (xm, cm, m),
+            2 => (m, cm, xm),
+            3 => (m, xm, cm),
+            4 => (xm, m, cm),
+            _ => (cm, m, xm),
+        };
+
+        Rgba {
+            r,
+            g,
+            b,
+            a: color.a,
+        }
+    }
 }
 
-pub fn white() -> Color {
-    Color(ColorU::white())
+impl TryFrom<&'_ str> for Rgba {
+    type Error = anyhow::Error;
+
+    fn try_from(value: &'_ str) -> Result<Self, Self::Error> {
+        const RGB: usize = "rgb".len();
+        const RGBA: usize = "rgba".len();
+        const RRGGBB: usize = "rrggbb".len();
+        const RRGGBBAA: usize = "rrggbbaa".len();
+
+        const EXPECTED_FORMATS: &str = "Expected #rgb, #rgba, #rrggbb, or #rrggbbaa";
+
+        let Some(("", hex)) = value.trim().split_once('#') else {
+            bail!("invalid RGBA hex color: '{value}'. {EXPECTED_FORMATS}");
+        };
+
+        let (r, g, b, a) = match hex.len() {
+            RGB | RGBA => {
+                let r = u8::from_str_radix(&hex[0..1], 16)?;
+                let g = u8::from_str_radix(&hex[1..2], 16)?;
+                let b = u8::from_str_radix(&hex[2..3], 16)?;
+                let a = if hex.len() == RGBA {
+                    u8::from_str_radix(&hex[3..4], 16)?
+                } else {
+                    0xf
+                };
+
+                /// Duplicates a given hex digit.
+                /// E.g., `0xf` -> `0xff`.
+                const fn duplicate(value: u8) -> u8 {
+                    value << 4 | value
+                }
+
+                (duplicate(r), duplicate(g), duplicate(b), duplicate(a))
+            }
+            RRGGBB | RRGGBBAA => {
+                let r = u8::from_str_radix(&hex[0..2], 16)?;
+                let g = u8::from_str_radix(&hex[2..4], 16)?;
+                let b = u8::from_str_radix(&hex[4..6], 16)?;
+                let a = if hex.len() == RRGGBBAA {
+                    u8::from_str_radix(&hex[6..8], 16)?
+                } else {
+                    0xff
+                };
+                (r, g, b, a)
+            }
+            _ => bail!("invalid RGBA hex color: '{value}'. {EXPECTED_FORMATS}"),
+        };
+
+        Ok(Rgba {
+            r: r as f32 / 255.,
+            g: g as f32 / 255.,
+            b: b as f32 / 255.,
+            a: a as f32 / 255.,
+        })
+    }
 }
 
-pub fn red() -> Color {
-    color(0xff0000ff)
+#[derive(Default, Copy, Clone, Debug)]
+#[repr(C)]
+pub struct Hsla {
+    pub h: f32,
+    pub s: f32,
+    pub l: f32,
+    pub a: f32,
 }
 
-pub fn green() -> Color {
-    color(0x00ff00ff)
+impl PartialEq for Hsla {
+    fn eq(&self, other: &Self) -> bool {
+        self.h
+            .total_cmp(&other.h)
+            .then(self.s.total_cmp(&other.s))
+            .then(self.l.total_cmp(&other.l).then(self.a.total_cmp(&other.a)))
+            .is_eq()
+    }
 }
 
-pub fn blue() -> Color {
-    color(0x0000ffff)
+impl PartialOrd for Hsla {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        // SAFETY: The total ordering relies on this always being Some()
+        Some(
+            self.h
+                .total_cmp(&other.h)
+                .then(self.s.total_cmp(&other.s))
+                .then(self.l.total_cmp(&other.l).then(self.a.total_cmp(&other.a))),
+        )
+    }
 }
 
-pub fn yellow() -> Color {
-    color(0xffff00ff)
+impl Ord for Hsla {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        // SAFETY: The partial comparison is a total comparison
+        unsafe { self.partial_cmp(other).unwrap_unchecked() }
+    }
 }
 
-impl Color {
-    pub fn transparent_black() -> Self {
-        transparent_black()
+impl Hsla {
+    pub fn to_rgb(self) -> Rgba {
+        self.into()
+    }
+
+    pub fn red() -> Self {
+        red()
+    }
+
+    pub fn green() -> Self {
+        green()
+    }
+
+    pub fn blue() -> Self {
+        blue()
     }
 
     pub fn black() -> Self {
@@ -70,107 +228,230 @@ impl Color {
         white()
     }
 
-    pub fn red() -> Self {
-        Color::from_u32(0xff0000ff)
+    pub fn transparent_black() -> Self {
+        transparent_black()
+    }
+}
+
+impl Eq for Hsla {}
+
+pub fn hsla(h: f32, s: f32, l: f32, a: f32) -> Hsla {
+    Hsla {
+        h: h.clamp(0., 1.),
+        s: s.clamp(0., 1.),
+        l: l.clamp(0., 1.),
+        a: a.clamp(0., 1.),
     }
+}
 
-    pub fn green() -> Self {
-        Color::from_u32(0x00ff00ff)
+pub fn black() -> Hsla {
+    Hsla {
+        h: 0.,
+        s: 0.,
+        l: 0.,
+        a: 1.,
     }
+}
 
-    pub fn blue() -> Self {
-        Color::from_u32(0x0000ffff)
+pub fn transparent_black() -> Hsla {
+    Hsla {
+        h: 0.,
+        s: 0.,
+        l: 0.,
+        a: 0.,
+    }
+}
+
+pub fn white() -> Hsla {
+    Hsla {
+        h: 0.,
+        s: 0.,
+        l: 1.,
+        a: 1.,
     }
+}
 
-    pub fn yellow() -> Self {
-        Color::from_u32(0xffff00ff)
+pub fn red() -> Hsla {
+    Hsla {
+        h: 0.,
+        s: 1.,
+        l: 0.5,
+        a: 1.,
     }
+}
 
-    pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
-        Self(ColorU::new(r, g, b, a))
+pub fn blue() -> Hsla {
+    Hsla {
+        h: 0.6,
+        s: 1.,
+        l: 0.5,
+        a: 1.,
     }
+}
 
-    pub fn from_u32(rgba: u32) -> Self {
-        Self(ColorU::from_u32(rgba))
+pub fn green() -> Hsla {
+    Hsla {
+        h: 0.33,
+        s: 1.,
+        l: 0.5,
+        a: 1.,
     }
+}
 
-    pub fn blend(source: Color, dest: Color) -> Color {
-        // Skip blending if we don't need it.
-        if source.a == 255 {
-            return source;
-        } else if source.a == 0 {
-            return dest;
-        }
+pub fn yellow() -> Hsla {
+    Hsla {
+        h: 0.16,
+        s: 1.,
+        l: 0.5,
+        a: 1.,
+    }
+}
 
-        let source = source.0.to_f32();
-        let dest = dest.0.to_f32();
+impl Hsla {
+    /// Returns true if the HSLA color is fully transparent, false otherwise.
+    pub fn is_transparent(&self) -> bool {
+        self.a == 0.0
+    }
 
-        let a = source.a() + (dest.a() * (1. - source.a()));
-        let r = ((source.r() * source.a()) + (dest.r() * dest.a() * (1. - source.a()))) / a;
-        let g = ((source.g() * source.a()) + (dest.g() * dest.a() * (1. - source.a()))) / a;
-        let b = ((source.b() * source.a()) + (dest.b() * dest.a() * (1. - source.a()))) / a;
+    /// Blends `other` on top of `self` based on `other`'s alpha value. The resulting color is a combination of `self`'s and `other`'s colors.
+    ///
+    /// If `other`'s alpha value is 1.0 or greater, `other` color is fully opaque, thus `other` is returned as the output color.
+    /// If `other`'s alpha value is 0.0 or less, `other` color is fully transparent, thus `self` is returned as the output color.
+    /// Else, the output color is calculated as a blend of `self` and `other` based on their weighted alpha values.
+    ///
+    /// Assumptions:
+    /// - Alpha values are contained in the range [0, 1], with 1 as fully opaque and 0 as fully transparent.
+    /// - The relative contributions of `self` and `other` is based on `self`'s alpha value (`self.a`) and `other`'s  alpha value (`other.a`), `self` contributing `self.a * (1.0 - other.a)` and `other` contributing it's own alpha value.
+    /// - RGB color components are contained in the range [0, 1].
+    /// - If `self` and `other` colors are out of the valid range, the blend operation's output and behavior is undefined.
+    pub fn blend(self, other: Hsla) -> Hsla {
+        let alpha = other.a;
+
+        if alpha >= 1.0 {
+            other
+        } else if alpha <= 0.0 {
+            return self;
+        } else {
+            let converted_self = Rgba::from(self);
+            let converted_other = Rgba::from(other);
+            let blended_rgb = converted_self.blend(converted_other);
+            return Hsla::from(blended_rgb);
+        }
+    }
 
-        Self(ColorF::new(r, g, b, a).to_u8())
+    /// Fade out the color by a given factor. This factor should be between 0.0 and 1.0.
+    /// Where 0.0 will leave the color unchanged, and 1.0 will completely fade out the color.
+    pub fn fade_out(&mut self, factor: f32) {
+        self.a *= 1.0 - factor.clamp(0., 1.);
     }
+}
 
-    pub fn fade_out(&mut self, fade: f32) {
-        let fade = fade.clamp(0., 1.);
-        self.0.a = (self.0.a as f32 * (1. - fade)) as u8;
+// impl From<Hsla> for Rgba {
+//     fn from(value: Hsla) -> Self {
+//         let h = value.h;
+//         let s = value.s;
+//         let l = value.l;
+
+//         let c = (1 - |2L - 1|) X s
+//     }
+// }
+
+impl From<Rgba> for Hsla {
+    fn from(color: Rgba) -> Self {
+        let r = color.r;
+        let g = color.g;
+        let b = color.b;
+
+        let max = r.max(g.max(b));
+        let min = r.min(g.min(b));
+        let delta = max - min;
+
+        let l = (max + min) / 2.0;
+        let s = if l == 0.0 || l == 1.0 {
+            0.0
+        } else if l < 0.5 {
+            delta / (2.0 * l)
+        } else {
+            delta / (2.0 - 2.0 * l)
+        };
+
+        let h = if delta == 0.0 {
+            0.0
+        } else if max == r {
+            ((g - b) / delta).rem_euclid(6.0) / 6.0
+        } else if max == g {
+            ((b - r) / delta + 2.0) / 6.0
+        } else {
+            ((r - g) / delta + 4.0) / 6.0
+        };
+
+        Hsla {
+            h,
+            s,
+            l,
+            a: color.a,
+        }
     }
 }
 
-impl<'de> Deserialize<'de> for Color {
+impl<'de> Deserialize<'de> for Hsla {
     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     where
         D: Deserializer<'de>,
     {
-        let literal: Cow<str> = Deserialize::deserialize(deserializer)?;
-        if let Some(digits) = literal.strip_prefix('#') {
-            if let Ok(value) = u32::from_str_radix(digits, 16) {
-                if digits.len() == 6 {
-                    return Ok(Color::from_u32((value << 8) | 0xFF));
-                } else if digits.len() == 8 {
-                    return Ok(Color::from_u32(value));
-                }
-            }
-        }
-        Err(de::Error::invalid_value(
-            Unexpected::Str(literal.as_ref()),
-            &"#RRGGBB[AA]",
-        ))
+        // First, deserialize it into Rgba
+        let rgba = Rgba::deserialize(deserializer)?;
+
+        // Then, use the From<Rgba> for Hsla implementation to convert it
+        Ok(Hsla::from(rgba))
     }
 }
 
-impl From<u32> for Color {
-    fn from(value: u32) -> Self {
-        Self(ColorU::from_u32(value))
+#[cfg(test)]
+mod tests {
+    use serde_json::json;
+
+    use super::*;
+
+    #[test]
+    fn test_deserialize_three_value_hex_to_rgba() {
+        let actual: Rgba = serde_json::from_value(json!("#f09")).unwrap();
+
+        assert_eq!(actual, rgba(0xff0099ff))
     }
-}
 
-impl ToJson for Color {
-    fn to_json(&self) -> serde_json::Value {
-        json!(format!(
-            "0x{:x}{:x}{:x}{:x}",
-            self.0.r, self.0.g, self.0.b, self.0.a
-        ))
+    #[test]
+    fn test_deserialize_four_value_hex_to_rgba() {
+        let actual: Rgba = serde_json::from_value(json!("#f09f")).unwrap();
+
+        assert_eq!(actual, rgba(0xff0099ff))
     }
-}
 
-impl Deref for Color {
-    type Target = ColorU;
-    fn deref(&self) -> &Self::Target {
-        &self.0
+    #[test]
+    fn test_deserialize_six_value_hex_to_rgba() {
+        let actual: Rgba = serde_json::from_value(json!("#ff0099")).unwrap();
+
+        assert_eq!(actual, rgba(0xff0099ff))
     }
-}
 
-impl DerefMut for Color {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.0
+    #[test]
+    fn test_deserialize_eight_value_hex_to_rgba() {
+        let actual: Rgba = serde_json::from_value(json!("#ff0099ff")).unwrap();
+
+        assert_eq!(actual, rgba(0xff0099ff))
     }
-}
 
-impl fmt::Debug for Color {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.0.fmt(f)
+    #[test]
+    fn test_deserialize_eight_value_hex_with_padding_to_rgba() {
+        let actual: Rgba = serde_json::from_value(json!(" #f5f5f5ff   ")).unwrap();
+
+        assert_eq!(actual, rgba(0xf5f5f5ff))
+    }
+
+    #[test]
+    fn test_deserialize_eight_value_hex_with_mixed_case_to_rgba() {
+        let actual: Rgba = serde_json::from_value(json!("#DeAdbEeF")).unwrap();
+
+        assert_eq!(actual, rgba(0xdeadbeef))
     }
 }

crates/gpui/src/elements.rs 🔗

@@ -1,740 +0,0 @@
-mod align;
-mod canvas;
-mod clipped;
-mod component;
-mod constrained_box;
-mod container;
-mod empty;
-mod expanded;
-mod flex;
-mod hook;
-mod image;
-mod keystroke_label;
-mod label;
-mod list;
-mod mouse_event_handler;
-mod overlay;
-mod resizable;
-mod stack;
-mod svg;
-mod text;
-mod tooltip;
-mod uniform_list;
-
-pub use self::{
-    align::*, canvas::*, component::*, constrained_box::*, container::*, empty::*, flex::*,
-    hook::*, image::*, keystroke_label::*, label::*, list::*, mouse_event_handler::*, overlay::*,
-    resizable::*, stack::*, svg::*, text::*, tooltip::*, uniform_list::*,
-};
-pub use crate::window::ChildView;
-
-use self::{clipped::Clipped, expanded::Expanded};
-use crate::{
-    geometry::{
-        rect::RectF,
-        vector::{vec2f, Vector2F},
-    },
-    json, Action, Entity, SizeConstraint, TypeTag, View, ViewContext, WeakViewHandle,
-    WindowContext,
-};
-use anyhow::{anyhow, Result};
-use core::panic;
-use json::ToJson;
-use std::{
-    any::{type_name, Any},
-    borrow::Cow,
-    mem,
-    ops::Range,
-};
-
-pub trait Element<V: 'static>: 'static {
-    type LayoutState;
-    type PaintState;
-
-    fn view_name(&self) -> &'static str {
-        type_name::<V>()
-    }
-
-    fn layout(
-        &mut self,
-        constraint: SizeConstraint,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> (Vector2F, Self::LayoutState);
-
-    fn paint(
-        &mut self,
-        bounds: RectF,
-        visible_bounds: RectF,
-        layout: &mut Self::LayoutState,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> Self::PaintState;
-
-    fn rect_for_text_range(
-        &self,
-        range_utf16: Range<usize>,
-        bounds: RectF,
-        visible_bounds: RectF,
-        layout: &Self::LayoutState,
-        paint: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> Option<RectF>;
-
-    fn metadata(&self) -> Option<&dyn Any> {
-        None
-    }
-
-    fn debug(
-        &self,
-        bounds: RectF,
-        layout: &Self::LayoutState,
-        paint: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> serde_json::Value;
-
-    fn into_any(self) -> AnyElement<V>
-    where
-        Self: 'static + Sized,
-    {
-        AnyElement {
-            state: Box::new(ElementState::Init { element: self }),
-            name: None,
-        }
-    }
-
-    fn into_any_named(self, name: impl Into<Cow<'static, str>>) -> AnyElement<V>
-    where
-        Self: 'static + Sized,
-    {
-        AnyElement {
-            state: Box::new(ElementState::Init { element: self }),
-            name: Some(name.into()),
-        }
-    }
-
-    fn into_root_element(self, cx: &ViewContext<V>) -> RootElement<V>
-    where
-        Self: 'static + Sized,
-    {
-        RootElement {
-            element: self.into_any(),
-            view: cx.handle().downgrade(),
-        }
-    }
-
-    fn constrained(self) -> ConstrainedBox<V>
-    where
-        Self: 'static + Sized,
-    {
-        ConstrainedBox::new(self.into_any())
-    }
-
-    fn aligned(self) -> Align<V>
-    where
-        Self: 'static + Sized,
-    {
-        Align::new(self.into_any())
-    }
-
-    fn clipped(self) -> Clipped<V>
-    where
-        Self: 'static + Sized,
-    {
-        Clipped::new(self.into_any())
-    }
-
-    fn contained(self) -> Container<V>
-    where
-        Self: 'static + Sized,
-    {
-        Container::new(self.into_any())
-    }
-
-    fn expanded(self) -> Expanded<V>
-    where
-        Self: 'static + Sized,
-    {
-        Expanded::new(self.into_any())
-    }
-
-    fn flex(self, flex: f32, expanded: bool) -> FlexItem<V>
-    where
-        Self: 'static + Sized,
-    {
-        FlexItem::new(self.into_any()).flex(flex, expanded)
-    }
-
-    fn flex_float(self) -> FlexItem<V>
-    where
-        Self: 'static + Sized,
-    {
-        FlexItem::new(self.into_any()).float()
-    }
-
-    fn with_dynamic_tooltip(
-        self,
-        tag: TypeTag,
-        id: usize,
-        text: impl Into<Cow<'static, str>>,
-        action: Option<Box<dyn Action>>,
-        style: TooltipStyle,
-        cx: &mut ViewContext<V>,
-    ) -> Tooltip<V>
-    where
-        Self: 'static + Sized,
-    {
-        Tooltip::new_dynamic(tag, id, text, action, style, self.into_any(), cx)
-    }
-    fn with_tooltip<Tag: 'static>(
-        self,
-        id: usize,
-        text: impl Into<Cow<'static, str>>,
-        action: Option<Box<dyn Action>>,
-        style: TooltipStyle,
-        cx: &mut ViewContext<V>,
-    ) -> Tooltip<V>
-    where
-        Self: 'static + Sized,
-    {
-        Tooltip::new::<Tag>(id, text, action, style, self.into_any(), cx)
-    }
-
-    /// Uses the the given element to calculate resizes for the given tag
-    fn provide_resize_bounds<Tag: 'static>(self) -> BoundsProvider<V, Tag>
-    where
-        Self: 'static + Sized,
-    {
-        BoundsProvider::<_, Tag>::new(self.into_any())
-    }
-
-    /// Calls the given closure with the new size of the element whenever the
-    /// handle is dragged. This will be calculated in relation to the bounds
-    /// provided by the given tag
-    fn resizable<Tag: 'static>(
-        self,
-        side: HandleSide,
-        size: f32,
-        on_resize: impl 'static + FnMut(&mut V, Option<f32>, &mut ViewContext<V>),
-    ) -> Resizable<V>
-    where
-        Self: 'static + Sized,
-    {
-        Resizable::new::<Tag>(self.into_any(), side, size, on_resize)
-    }
-
-    fn mouse<Tag: 'static>(self, region_id: usize) -> MouseEventHandler<V>
-    where
-        Self: Sized,
-    {
-        MouseEventHandler::for_child::<Tag>(self.into_any(), region_id)
-    }
-
-    fn component(self) -> StatelessElementAdapter
-    where
-        Self: Sized,
-    {
-        StatelessElementAdapter::new(self.into_any())
-    }
-
-    fn stateful_component(self) -> StatefulElementAdapter<V>
-    where
-        Self: Sized,
-    {
-        StatefulElementAdapter::new(self.into_any())
-    }
-
-    fn styleable_component(self) -> StylableAdapter<StatelessElementAdapter>
-    where
-        Self: Sized,
-    {
-        StatelessElementAdapter::new(self.into_any()).stylable()
-    }
-}
-
-trait AnyElementState<V> {
-    fn layout(
-        &mut self,
-        constraint: SizeConstraint,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> Vector2F;
-
-    fn paint(
-        &mut self,
-        origin: Vector2F,
-        visible_bounds: RectF,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    );
-
-    fn rect_for_text_range(
-        &self,
-        range_utf16: Range<usize>,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> Option<RectF>;
-
-    fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value;
-
-    fn size(&self) -> Vector2F;
-
-    fn metadata(&self) -> Option<&dyn Any>;
-}
-
-enum ElementState<V: 'static, E: Element<V>> {
-    Empty,
-    Init {
-        element: E,
-    },
-    PostLayout {
-        element: E,
-        constraint: SizeConstraint,
-        size: Vector2F,
-        layout: E::LayoutState,
-    },
-    PostPaint {
-        element: E,
-        constraint: SizeConstraint,
-        bounds: RectF,
-        visible_bounds: RectF,
-        layout: E::LayoutState,
-        paint: E::PaintState,
-    },
-}
-
-impl<V, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
-    fn layout(
-        &mut self,
-        constraint: SizeConstraint,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> Vector2F {
-        let result;
-        *self = match mem::take(self) {
-            ElementState::Empty => unreachable!(),
-            ElementState::Init { mut element }
-            | ElementState::PostLayout { mut element, .. }
-            | ElementState::PostPaint { mut element, .. } => {
-                let (size, layout) = element.layout(constraint, view, cx);
-                debug_assert!(
-                    size.x().is_finite(),
-                    "Element for {:?} had infinite x size after layout",
-                    element.view_name()
-                );
-                debug_assert!(
-                    size.y().is_finite(),
-                    "Element for {:?} had infinite y size after layout",
-                    element.view_name()
-                );
-
-                result = size;
-                ElementState::PostLayout {
-                    element,
-                    constraint,
-                    size,
-                    layout,
-                }
-            }
-        };
-        result
-    }
-
-    fn paint(
-        &mut self,
-        origin: Vector2F,
-        visible_bounds: RectF,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) {
-        *self = match mem::take(self) {
-            ElementState::PostLayout {
-                mut element,
-                constraint,
-                size,
-                mut layout,
-            } => {
-                let bounds = RectF::new(origin, size);
-                let paint = element.paint(bounds, visible_bounds, &mut layout, view, cx);
-                ElementState::PostPaint {
-                    element,
-                    constraint,
-                    bounds,
-                    visible_bounds,
-                    layout,
-                    paint,
-                }
-            }
-            ElementState::PostPaint {
-                mut element,
-                constraint,
-                bounds,
-                mut layout,
-                ..
-            } => {
-                let bounds = RectF::new(origin, bounds.size());
-                let paint = element.paint(bounds, visible_bounds, &mut layout, view, cx);
-                ElementState::PostPaint {
-                    element,
-                    constraint,
-                    bounds,
-                    visible_bounds,
-                    layout,
-                    paint,
-                }
-            }
-            ElementState::Empty => panic!("invalid element lifecycle state"),
-            ElementState::Init { .. } => {
-                panic!("invalid element lifecycle state, paint called before layout")
-            }
-        }
-    }
-
-    fn rect_for_text_range(
-        &self,
-        range_utf16: Range<usize>,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> Option<RectF> {
-        if let ElementState::PostPaint {
-            element,
-            bounds,
-            visible_bounds,
-            layout,
-            paint,
-            ..
-        } = self
-        {
-            element.rect_for_text_range(
-                range_utf16,
-                *bounds,
-                *visible_bounds,
-                layout,
-                paint,
-                view,
-                cx,
-            )
-        } else {
-            None
-        }
-    }
-
-    fn size(&self) -> Vector2F {
-        match self {
-            ElementState::Empty | ElementState::Init { .. } => {
-                panic!("invalid element lifecycle state")
-            }
-            ElementState::PostLayout { size, .. } => *size,
-            ElementState::PostPaint { bounds, .. } => bounds.size(),
-        }
-    }
-
-    fn metadata(&self) -> Option<&dyn Any> {
-        match self {
-            ElementState::Empty => unreachable!(),
-            ElementState::Init { element }
-            | ElementState::PostLayout { element, .. }
-            | ElementState::PostPaint { element, .. } => element.metadata(),
-        }
-    }
-
-    fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value {
-        match self {
-            ElementState::PostPaint {
-                element,
-                constraint,
-                bounds,
-                visible_bounds,
-                layout,
-                paint,
-            } => {
-                let mut value = element.debug(*bounds, layout, paint, view, cx);
-                if let json::Value::Object(map) = &mut value {
-                    let mut new_map: crate::json::Map<String, serde_json::Value> =
-                        Default::default();
-                    if let Some(typ) = map.remove("type") {
-                        new_map.insert("type".into(), typ);
-                    }
-                    new_map.insert("constraint".into(), constraint.to_json());
-                    new_map.insert("bounds".into(), bounds.to_json());
-                    new_map.insert("visible_bounds".into(), visible_bounds.to_json());
-                    new_map.append(map);
-                    json::Value::Object(new_map)
-                } else {
-                    value
-                }
-            }
-
-            _ => panic!("invalid element lifecycle state"),
-        }
-    }
-}
-
-impl<V, E: Element<V>> Default for ElementState<V, E> {
-    fn default() -> Self {
-        Self::Empty
-    }
-}
-
-pub struct AnyElement<V> {
-    state: Box<dyn AnyElementState<V>>,
-    name: Option<Cow<'static, str>>,
-}
-
-impl<V> AnyElement<V> {
-    pub fn name(&self) -> Option<&str> {
-        self.name.as_deref()
-    }
-
-    pub fn metadata<T: 'static>(&self) -> Option<&T> {
-        self.state
-            .metadata()
-            .and_then(|data| data.downcast_ref::<T>())
-    }
-
-    pub fn layout(
-        &mut self,
-        constraint: SizeConstraint,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> Vector2F {
-        self.state.layout(constraint, view, cx)
-    }
-
-    pub fn paint(
-        &mut self,
-        origin: Vector2F,
-        visible_bounds: RectF,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) {
-        self.state.paint(origin, visible_bounds, view, cx);
-    }
-
-    pub fn rect_for_text_range(
-        &self,
-        range_utf16: Range<usize>,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> Option<RectF> {
-        self.state.rect_for_text_range(range_utf16, view, cx)
-    }
-
-    pub fn size(&self) -> Vector2F {
-        self.state.size()
-    }
-
-    pub fn debug(&self, view: &V, cx: &ViewContext<V>) -> json::Value {
-        let mut value = self.state.debug(view, cx);
-
-        if let Some(name) = &self.name {
-            if let json::Value::Object(map) = &mut value {
-                let mut new_map: crate::json::Map<String, serde_json::Value> = Default::default();
-                new_map.insert("name".into(), json::Value::String(name.to_string()));
-                new_map.append(map);
-                return json::Value::Object(new_map);
-            }
-        }
-
-        value
-    }
-
-    pub fn with_metadata<T, F, R>(&self, f: F) -> R
-    where
-        T: 'static,
-        F: FnOnce(Option<&T>) -> R,
-    {
-        f(self.state.metadata().and_then(|m| m.downcast_ref()))
-    }
-}
-
-impl<V: 'static> Element<V> for AnyElement<V> {
-    type LayoutState = ();
-    type PaintState = ();
-
-    fn layout(
-        &mut self,
-        constraint: SizeConstraint,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> (Vector2F, Self::LayoutState) {
-        let size = self.layout(constraint, view, cx);
-        (size, ())
-    }
-
-    fn paint(
-        &mut self,
-        bounds: RectF,
-        visible_bounds: RectF,
-        _: &mut Self::LayoutState,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> Self::PaintState {
-        self.paint(bounds.origin(), visible_bounds, view, cx);
-    }
-
-    fn rect_for_text_range(
-        &self,
-        range_utf16: Range<usize>,
-        _: RectF,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> Option<RectF> {
-        self.rect_for_text_range(range_utf16, view, cx)
-    }
-
-    fn debug(
-        &self,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> serde_json::Value {
-        self.debug(view, cx)
-    }
-
-    fn into_any(self) -> AnyElement<V>
-    where
-        Self: Sized,
-    {
-        self
-    }
-}
-
-impl Entity for AnyElement<()> {
-    type Event = ();
-}
-
-// impl View for AnyElement<()> {}
-
-pub struct RootElement<V> {
-    element: AnyElement<V>,
-    view: WeakViewHandle<V>,
-}
-
-impl<V> RootElement<V> {
-    pub fn new(element: AnyElement<V>, view: WeakViewHandle<V>) -> Self {
-        Self { element, view }
-    }
-}
-
-pub trait AnyRootElement {
-    fn layout(&mut self, constraint: SizeConstraint, cx: &mut WindowContext) -> Result<Vector2F>;
-    fn paint(
-        &mut self,
-        origin: Vector2F,
-        visible_bounds: RectF,
-        cx: &mut WindowContext,
-    ) -> Result<()>;
-    fn rect_for_text_range(
-        &self,
-        range_utf16: Range<usize>,
-        cx: &WindowContext,
-    ) -> Result<Option<RectF>>;
-    fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value>;
-    fn name(&self) -> Option<&str>;
-}
-
-impl<V: View> AnyRootElement for RootElement<V> {
-    fn layout(&mut self, constraint: SizeConstraint, cx: &mut WindowContext) -> Result<Vector2F> {
-        let view = self
-            .view
-            .upgrade(cx)
-            .ok_or_else(|| anyhow!("layout called on a root element for a dropped view"))?;
-        view.update(cx, |view, cx| Ok(self.element.layout(constraint, view, cx)))
-    }
-
-    fn paint(
-        &mut self,
-        origin: Vector2F,
-        visible_bounds: RectF,
-        cx: &mut WindowContext,
-    ) -> Result<()> {
-        let view = self
-            .view
-            .upgrade(cx)
-            .ok_or_else(|| anyhow!("paint called on a root element for a dropped view"))?;
-
-        view.update(cx, |view, cx| {
-            self.element.paint(origin, visible_bounds, view, cx);
-            Ok(())
-        })
-    }
-
-    fn rect_for_text_range(
-        &self,
-        range_utf16: Range<usize>,
-        cx: &WindowContext,
-    ) -> Result<Option<RectF>> {
-        let view = self.view.upgrade(cx).ok_or_else(|| {
-            anyhow!("rect_for_text_range called on a root element for a dropped view")
-        })?;
-        let view = view.read(cx);
-        let view_context = ViewContext::immutable(cx, self.view.id());
-        Ok(self
-            .element
-            .rect_for_text_range(range_utf16, view, &view_context))
-    }
-
-    fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value> {
-        let view = self
-            .view
-            .upgrade(cx)
-            .ok_or_else(|| anyhow!("debug called on a root element for a dropped view"))?;
-        let view = view.read(cx);
-        let view_context = ViewContext::immutable(cx, self.view.id());
-        Ok(serde_json::json!({
-            "view_id": self.view.id(),
-            "view_name": V::ui_name(),
-            "view": view.debug_json(cx),
-            "element": self.element.debug(view, &view_context)
-        }))
-    }
-
-    fn name(&self) -> Option<&str> {
-        self.element.name()
-    }
-}
-
-pub trait ParentElement<'a, V: 'static>: Extend<AnyElement<V>> + Sized {
-    fn add_children<E: Element<V>>(&mut self, children: impl IntoIterator<Item = E>) {
-        self.extend(children.into_iter().map(|child| child.into_any()));
-    }
-
-    fn add_child<D: Element<V>>(&mut self, child: D) {
-        self.extend(Some(child.into_any()));
-    }
-
-    fn with_children<D: Element<V>>(mut self, children: impl IntoIterator<Item = D>) -> Self {
-        self.extend(children.into_iter().map(|child| child.into_any()));
-        self
-    }
-
-    fn with_child<D: Element<V>>(mut self, child: D) -> Self {
-        self.extend(Some(child.into_any()));
-        self
-    }
-}
-
-impl<'a, V, T> ParentElement<'a, V> for T
-where
-    V: 'static,
-    T: Extend<AnyElement<V>>,
-{
-}
-
-pub fn constrain_size_preserving_aspect_ratio(max_size: Vector2F, size: Vector2F) -> Vector2F {
-    if max_size.x().is_infinite() && max_size.y().is_infinite() {
-        size
-    } else if max_size.x().is_infinite() || max_size.x() / max_size.y() > size.x() / size.y() {
-        vec2f(size.x() * max_size.y() / size.y(), max_size.y())
-    } else {
-        vec2f(max_size.x(), size.y() * max_size.x() / size.x())
-    }
-}

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

@@ -1,115 +0,0 @@
-use crate::{
-    geometry::{rect::RectF, vector::Vector2F},
-    json, AnyElement, Element, SizeConstraint, ViewContext,
-};
-use json::ToJson;
-
-use serde_json::json;
-
-pub struct Align<V> {
-    child: AnyElement<V>,
-    alignment: Vector2F,
-}
-
-impl<V> Align<V> {
-    pub fn new(child: AnyElement<V>) -> Self {
-        Self {
-            child,
-            alignment: Vector2F::zero(),
-        }
-    }
-
-    pub fn top(mut self) -> Self {
-        self.alignment.set_y(-1.0);
-        self
-    }
-
-    pub fn bottom(mut self) -> Self {
-        self.alignment.set_y(1.0);
-        self
-    }
-
-    pub fn left(mut self) -> Self {
-        self.alignment.set_x(-1.0);
-        self
-    }
-
-    pub fn right(mut self) -> Self {
-        self.alignment.set_x(1.0);
-        self
-    }
-}
-
-impl<V: 'static> Element<V> for Align<V> {
-    type LayoutState = ();
-    type PaintState = ();
-
-    fn layout(
-        &mut self,
-        mut constraint: SizeConstraint,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> (Vector2F, Self::LayoutState) {
-        let mut size = constraint.max;
-        constraint.min = Vector2F::zero();
-        let child_size = self.child.layout(constraint, view, cx);
-        if size.x().is_infinite() {
-            size.set_x(child_size.x());
-        }
-        if size.y().is_infinite() {
-            size.set_y(child_size.y());
-        }
-        (size, ())
-    }
-
-    fn paint(
-        &mut self,
-        bounds: RectF,
-        visible_bounds: RectF,
-        _: &mut Self::LayoutState,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> Self::PaintState {
-        let my_center = bounds.size() / 2.;
-        let my_target = my_center + my_center * self.alignment;
-
-        let child_center = self.child.size() / 2.;
-        let child_target = child_center + child_center * self.alignment;
-
-        self.child.paint(
-            bounds.origin() - (child_target - my_target),
-            visible_bounds,
-            view,
-            cx,
-        );
-    }
-
-    fn rect_for_text_range(
-        &self,
-        range_utf16: std::ops::Range<usize>,
-        _: RectF,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> Option<RectF> {
-        self.child.rect_for_text_range(range_utf16, view, cx)
-    }
-
-    fn debug(
-        &self,
-        bounds: pathfinder_geometry::rect::RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> json::Value {
-        json!({
-            "type": "Align",
-            "bounds": bounds.to_json(),
-            "alignment": self.alignment.to_json(),
-            "child": self.child.debug(view, cx),
-        })
-    }
-}

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

@@ -1,85 +1,54 @@
-use std::marker::PhantomData;
+use refineable::Refineable as _;
 
-use super::Element;
-use crate::{
-    json::{self, json},
-    ViewContext,
-};
-use json::ToJson;
-use pathfinder_geometry::{
-    rect::RectF,
-    vector::{vec2f, Vector2F},
-};
+use crate::{Bounds, Element, IntoElement, Pixels, Style, StyleRefinement, Styled, WindowContext};
 
-pub struct Canvas<V, F>(F, PhantomData<V>);
-
-impl<V, F> Canvas<V, F>
-where
-    F: FnMut(RectF, RectF, &mut V, &mut ViewContext<V>),
-{
-    pub fn new(f: F) -> Self {
-        Self(f, PhantomData)
+pub fn canvas(callback: impl 'static + FnOnce(&Bounds<Pixels>, &mut WindowContext)) -> Canvas {
+    Canvas {
+        paint_callback: Some(Box::new(callback)),
+        style: StyleRefinement::default(),
     }
 }
 
-impl<V: 'static, F> Element<V> for Canvas<V, F>
-where
-    F: 'static + FnMut(RectF, RectF, &mut V, &mut ViewContext<V>),
-{
-    type LayoutState = ();
-    type PaintState = ();
+pub struct Canvas {
+    paint_callback: Option<Box<dyn FnOnce(&Bounds<Pixels>, &mut WindowContext)>>,
+    style: StyleRefinement,
+}
 
-    fn layout(
-        &mut self,
-        constraint: crate::SizeConstraint,
-        _: &mut V,
-        _: &mut crate::ViewContext<V>,
-    ) -> (Vector2F, Self::LayoutState) {
-        let x = if constraint.max.x().is_finite() {
-            constraint.max.x()
-        } else {
-            constraint.min.x()
-        };
-        let y = if constraint.max.y().is_finite() {
-            constraint.max.y()
-        } else {
-            constraint.min.y()
-        };
-        (vec2f(x, y), ())
+impl IntoElement for Canvas {
+    type Element = Self;
+
+    fn element_id(&self) -> Option<crate::ElementId> {
+        None
     }
 
-    fn paint(
+    fn into_element(self) -> Self::Element {
+        self
+    }
+}
+
+impl Element for Canvas {
+    type State = Style;
+
+    fn request_layout(
         &mut self,
-        bounds: RectF,
-        visible_bounds: RectF,
-        _: &mut Self::LayoutState,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> Self::PaintState {
-        self.0(bounds, visible_bounds, view, cx)
+        _: Option<Self::State>,
+        cx: &mut WindowContext,
+    ) -> (crate::LayoutId, Self::State) {
+        let mut style = Style::default();
+        style.refine(&self.style);
+        let layout_id = cx.request_layout(&style, []);
+        (layout_id, style)
     }
 
-    fn rect_for_text_range(
-        &self,
-        _: std::ops::Range<usize>,
-        _: RectF,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        _: &V,
-        _: &ViewContext<V>,
-    ) -> Option<RectF> {
-        None
+    fn paint(&mut self, bounds: Bounds<Pixels>, style: &mut Style, cx: &mut WindowContext) {
+        style.paint(bounds, cx, |cx| {
+            (self.paint_callback.take().unwrap())(&bounds, cx)
+        });
     }
+}
 
-    fn debug(
-        &self,
-        bounds: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        _: &V,
-        _: &ViewContext<V>,
-    ) -> json::Value {
-        json!({"type": "Canvas", "bounds": bounds.to_json()})
+impl Styled for Canvas {
+    fn style(&mut self) -> &mut crate::StyleRefinement {
+        &mut self.style
     }
 }

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

@@ -1,71 +0,0 @@
-use std::ops::Range;
-
-use pathfinder_geometry::{rect::RectF, vector::Vector2F};
-use serde_json::json;
-
-use crate::{json, AnyElement, Element, SizeConstraint, ViewContext};
-
-pub struct Clipped<V> {
-    child: AnyElement<V>,
-}
-
-impl<V> Clipped<V> {
-    pub fn new(child: AnyElement<V>) -> Self {
-        Self { child }
-    }
-}
-
-impl<V: 'static> Element<V> for Clipped<V> {
-    type LayoutState = ();
-    type PaintState = ();
-
-    fn layout(
-        &mut self,
-        constraint: SizeConstraint,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> (Vector2F, Self::LayoutState) {
-        (self.child.layout(constraint, view, cx), ())
-    }
-
-    fn paint(
-        &mut self,
-        bounds: RectF,
-        visible_bounds: RectF,
-        _: &mut Self::LayoutState,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> Self::PaintState {
-        cx.scene().push_layer(Some(bounds));
-        let state = self.child.paint(bounds.origin(), visible_bounds, view, cx);
-        cx.scene().pop_layer();
-        state
-    }
-
-    fn rect_for_text_range(
-        &self,
-        range_utf16: Range<usize>,
-        _: RectF,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> Option<RectF> {
-        self.child.rect_for_text_range(range_utf16, view, cx)
-    }
-
-    fn debug(
-        &self,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> json::Value {
-        json!({
-            "type": "Clipped",
-            "child": self.child.debug(view, cx)
-        })
-    }
-}

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

@@ -1,342 +0,0 @@
-use std::{any::Any, marker::PhantomData};
-
-use pathfinder_geometry::{rect::RectF, vector::Vector2F};
-
-use crate::{AnyElement, Element, SizeConstraint, ViewContext};
-
-use super::Empty;
-
-/// The core stateless component trait, simply rendering an element tree
-pub trait Component {
-    fn render<V: 'static>(self, cx: &mut ViewContext<V>) -> AnyElement<V>;
-
-    fn element<V: 'static>(self) -> ComponentAdapter<V, Self>
-    where
-        Self: Sized,
-    {
-        ComponentAdapter::new(self)
-    }
-
-    fn stylable(self) -> StylableAdapter<Self>
-    where
-        Self: Sized,
-    {
-        StylableAdapter::new(self)
-    }
-
-    fn stateful<V: 'static>(self) -> StatefulAdapter<Self, V>
-    where
-        Self: Sized,
-    {
-        StatefulAdapter::new(self)
-    }
-}
-
-/// Allows a a component's styles to be rebound in a simple way.
-pub trait Stylable: Component {
-    type Style: Clone;
-
-    fn with_style(self, style: Self::Style) -> Self;
-}
-
-/// This trait models the typestate pattern for a component's style,
-/// enforcing at compile time that a component is only usable after
-/// it has been styled while still allowing for late binding of the
-/// styling information
-pub trait SafeStylable {
-    type Style: Clone;
-    type Output: Component;
-
-    fn with_style(self, style: Self::Style) -> Self::Output;
-}
-
-/// All stylable components can trivially implement SafeStylable
-impl<C: Stylable> SafeStylable for C {
-    type Style = C::Style;
-
-    type Output = C;
-
-    fn with_style(self, style: Self::Style) -> Self::Output {
-        self.with_style(style)
-    }
-}
-
-/// Allows converting an unstylable component into a stylable one
-/// by using `()` as the style type
-pub struct StylableAdapter<C: Component> {
-    component: C,
-}
-
-impl<C: Component> StylableAdapter<C> {
-    pub fn new(component: C) -> Self {
-        Self { component }
-    }
-}
-
-impl<C: Component> SafeStylable for StylableAdapter<C> {
-    type Style = ();
-
-    type Output = C;
-
-    fn with_style(self, _: Self::Style) -> Self::Output {
-        self.component
-    }
-}
-
-/// This is a secondary trait for components that can be styled
-/// which rely on their view's state. This is useful for components that, for example,
-/// want to take click handler callbacks Unfortunately, the generic bound on the
-/// Component trait makes it incompatible with the stateless components above.
-// So let's just replicate them for now
-pub trait StatefulComponent<V: 'static> {
-    fn render(self, v: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
-
-    fn element(self) -> ComponentAdapter<V, Self>
-    where
-        Self: Sized,
-    {
-        ComponentAdapter::new(self)
-    }
-
-    fn styleable(self) -> StatefulStylableAdapter<Self, V>
-    where
-        Self: Sized,
-    {
-        StatefulStylableAdapter::new(self)
-    }
-
-    fn stateless(self) -> StatelessElementAdapter
-    where
-        Self: Sized + 'static,
-    {
-        StatelessElementAdapter::new(self.element().into_any())
-    }
-}
-
-/// It is trivial to convert stateless components to stateful components, so lets
-/// do so en masse. Note that the reverse is impossible without a helper.
-impl<V: 'static, C: Component> StatefulComponent<V> for C {
-    fn render(self, _: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V> {
-        self.render(cx)
-    }
-}
-
-/// Same as stylable, but generic over a view type
-pub trait StatefulStylable<V: 'static>: StatefulComponent<V> {
-    type Style: Clone;
-
-    fn with_style(self, style: Self::Style) -> Self;
-}
-
-/// Same as SafeStylable, but generic over a view type
-pub trait StatefulSafeStylable<V: 'static> {
-    type Style: Clone;
-    type Output: StatefulComponent<V>;
-
-    fn with_style(self, style: Self::Style) -> Self::Output;
-}
-
-/// Converting from stateless to stateful
-impl<V: 'static, C: SafeStylable> StatefulSafeStylable<V> for C {
-    type Style = C::Style;
-
-    type Output = C::Output;
-
-    fn with_style(self, style: Self::Style) -> Self::Output {
-        self.with_style(style)
-    }
-}
-
-// A helper for converting stateless components into stateful ones
-pub struct StatefulAdapter<C, V> {
-    component: C,
-    phantom: std::marker::PhantomData<V>,
-}
-
-impl<C: Component, V: 'static> StatefulAdapter<C, V> {
-    pub fn new(component: C) -> Self {
-        Self {
-            component,
-            phantom: std::marker::PhantomData,
-        }
-    }
-}
-
-impl<C: Component, V: 'static> StatefulComponent<V> for StatefulAdapter<C, V> {
-    fn render(self, _: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V> {
-        self.component.render(cx)
-    }
-}
-
-// A helper for converting stateful but style-less components into stylable ones
-// by using `()` as the style type
-pub struct StatefulStylableAdapter<C: StatefulComponent<V>, V: 'static> {
-    component: C,
-    phantom: std::marker::PhantomData<V>,
-}
-
-impl<C: StatefulComponent<V>, V: 'static> StatefulStylableAdapter<C, V> {
-    pub fn new(component: C) -> Self {
-        Self {
-            component,
-            phantom: std::marker::PhantomData,
-        }
-    }
-}
-
-impl<C: StatefulComponent<V>, V: 'static> StatefulSafeStylable<V>
-    for StatefulStylableAdapter<C, V>
-{
-    type Style = ();
-
-    type Output = C;
-
-    fn with_style(self, _: Self::Style) -> Self::Output {
-        self.component
-    }
-}
-
-/// A way of erasing the view generic from an element, useful
-/// for wrapping up an explicit element tree into stateless
-/// components
-pub struct StatelessElementAdapter {
-    element: Box<dyn Any>,
-}
-
-impl StatelessElementAdapter {
-    pub fn new<V: 'static>(element: AnyElement<V>) -> Self {
-        StatelessElementAdapter {
-            element: Box::new(element) as Box<dyn Any>,
-        }
-    }
-}
-
-impl Component for StatelessElementAdapter {
-    fn render<V: 'static>(self, _: &mut ViewContext<V>) -> AnyElement<V> {
-        *self
-            .element
-            .downcast::<AnyElement<V>>()
-            .expect("Don't move elements out of their view :(")
-    }
-}
-
-// For converting elements into stateful components
-pub struct StatefulElementAdapter<V: 'static> {
-    element: AnyElement<V>,
-    _phantom: std::marker::PhantomData<V>,
-}
-
-impl<V: 'static> StatefulElementAdapter<V> {
-    pub fn new(element: AnyElement<V>) -> Self {
-        Self {
-            element,
-            _phantom: std::marker::PhantomData,
-        }
-    }
-}
-
-impl<V: 'static> StatefulComponent<V> for StatefulElementAdapter<V> {
-    fn render(self, _: &mut V, _: &mut ViewContext<V>) -> AnyElement<V> {
-        self.element
-    }
-}
-
-/// A convenient shorthand for creating an empty component.
-impl Component for () {
-    fn render<V: 'static>(self, _: &mut ViewContext<V>) -> AnyElement<V> {
-        Empty::new().into_any()
-    }
-}
-
-impl Stylable for () {
-    type Style = ();
-
-    fn with_style(self, _: Self::Style) -> Self {
-        ()
-    }
-}
-
-// For converting components back into Elements
-pub struct ComponentAdapter<V: 'static, E> {
-    component: Option<E>,
-    element: Option<AnyElement<V>>,
-    phantom: PhantomData<V>,
-}
-
-impl<E, V: 'static> ComponentAdapter<V, E> {
-    pub fn new(e: E) -> Self {
-        Self {
-            component: Some(e),
-            element: None,
-            phantom: PhantomData,
-        }
-    }
-}
-
-impl<V: 'static, C: StatefulComponent<V> + 'static> Element<V> for ComponentAdapter<V, C> {
-    type LayoutState = ();
-
-    type PaintState = ();
-
-    fn layout(
-        &mut self,
-        constraint: SizeConstraint,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> (Vector2F, Self::LayoutState) {
-        if self.element.is_none() {
-            let element = self
-                .component
-                .take()
-                .expect("Component can only be rendered once")
-                .render(view, cx);
-            self.element = Some(element);
-        }
-        let constraint = self.element.as_mut().unwrap().layout(constraint, view, cx);
-        (constraint, ())
-    }
-
-    fn paint(
-        &mut self,
-        bounds: RectF,
-        visible_bounds: RectF,
-        _: &mut Self::LayoutState,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> Self::PaintState {
-        self.element
-            .as_mut()
-            .expect("Layout should always be called before paint")
-            .paint(bounds.origin(), visible_bounds, view, cx)
-    }
-
-    fn rect_for_text_range(
-        &self,
-        range_utf16: std::ops::Range<usize>,
-        _: RectF,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> Option<RectF> {
-        self.element
-            .as_ref()
-            .and_then(|el| el.rect_for_text_range(range_utf16, view, cx))
-    }
-
-    fn debug(
-        &self,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> serde_json::Value {
-        serde_json::json!({
-            "type": "ComponentAdapter",
-            "component": std::any::type_name::<C>(),
-            "child": self.element.as_ref().map(|el| el.debug(view, cx)),
-        })
-    }
-}

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

@@ -1,187 +0,0 @@
-use std::ops::Range;
-
-use json::ToJson;
-use serde_json::json;
-
-use crate::{
-    geometry::{rect::RectF, vector::Vector2F},
-    json, AnyElement, Element, SizeConstraint, ViewContext,
-};
-
-pub struct ConstrainedBox<V> {
-    child: AnyElement<V>,
-    constraint: Constraint<V>,
-}
-
-pub enum Constraint<V> {
-    Static(SizeConstraint),
-    Dynamic(Box<dyn FnMut(SizeConstraint, &mut V, &mut ViewContext<V>) -> SizeConstraint>),
-}
-
-impl<V> ToJson for Constraint<V> {
-    fn to_json(&self) -> serde_json::Value {
-        match self {
-            Constraint::Static(constraint) => constraint.to_json(),
-            Constraint::Dynamic(_) => "dynamic".into(),
-        }
-    }
-}
-
-impl<V: 'static> ConstrainedBox<V> {
-    pub fn new(child: impl Element<V>) -> Self {
-        Self {
-            child: child.into_any(),
-            constraint: Constraint::Static(Default::default()),
-        }
-    }
-
-    pub fn dynamically(
-        mut self,
-        constraint: impl 'static + FnMut(SizeConstraint, &mut V, &mut ViewContext<V>) -> SizeConstraint,
-    ) -> Self {
-        self.constraint = Constraint::Dynamic(Box::new(constraint));
-        self
-    }
-
-    pub fn with_min_width(mut self, min_width: f32) -> Self {
-        if let Constraint::Dynamic(_) = self.constraint {
-            self.constraint = Constraint::Static(Default::default());
-        }
-
-        if let Constraint::Static(constraint) = &mut self.constraint {
-            constraint.min.set_x(min_width);
-        } else {
-            unreachable!()
-        }
-
-        self
-    }
-
-    pub fn with_max_width(mut self, max_width: f32) -> Self {
-        if let Constraint::Dynamic(_) = self.constraint {
-            self.constraint = Constraint::Static(Default::default());
-        }
-
-        if let Constraint::Static(constraint) = &mut self.constraint {
-            constraint.max.set_x(max_width);
-        } else {
-            unreachable!()
-        }
-
-        self
-    }
-
-    pub fn with_max_height(mut self, max_height: f32) -> Self {
-        if let Constraint::Dynamic(_) = self.constraint {
-            self.constraint = Constraint::Static(Default::default());
-        }
-
-        if let Constraint::Static(constraint) = &mut self.constraint {
-            constraint.max.set_y(max_height);
-        } else {
-            unreachable!()
-        }
-
-        self
-    }
-
-    pub fn with_width(mut self, width: f32) -> Self {
-        if let Constraint::Dynamic(_) = self.constraint {
-            self.constraint = Constraint::Static(Default::default());
-        }
-
-        if let Constraint::Static(constraint) = &mut self.constraint {
-            constraint.min.set_x(width);
-            constraint.max.set_x(width);
-        } else {
-            unreachable!()
-        }
-
-        self
-    }
-
-    pub fn with_height(mut self, height: f32) -> Self {
-        if let Constraint::Dynamic(_) = self.constraint {
-            self.constraint = Constraint::Static(Default::default());
-        }
-
-        if let Constraint::Static(constraint) = &mut self.constraint {
-            constraint.min.set_y(height);
-            constraint.max.set_y(height);
-        } else {
-            unreachable!()
-        }
-
-        self
-    }
-
-    fn constraint(
-        &mut self,
-        input_constraint: SizeConstraint,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> SizeConstraint {
-        match &mut self.constraint {
-            Constraint::Static(constraint) => *constraint,
-            Constraint::Dynamic(compute_constraint) => {
-                compute_constraint(input_constraint, view, cx)
-            }
-        }
-    }
-}
-
-impl<V: 'static> Element<V> for ConstrainedBox<V> {
-    type LayoutState = ();
-    type PaintState = ();
-
-    fn layout(
-        &mut self,
-        mut parent_constraint: SizeConstraint,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> (Vector2F, Self::LayoutState) {
-        let constraint = self.constraint(parent_constraint, view, cx);
-        parent_constraint.min = parent_constraint.min.max(constraint.min);
-        parent_constraint.max = parent_constraint.max.min(constraint.max);
-        parent_constraint.max = parent_constraint.max.max(parent_constraint.min);
-        let size = self.child.layout(parent_constraint, view, cx);
-        (size, ())
-    }
-
-    fn paint(
-        &mut self,
-        bounds: RectF,
-        visible_bounds: RectF,
-        _: &mut Self::LayoutState,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> Self::PaintState {
-        cx.scene().push_layer(Some(visible_bounds));
-        self.child.paint(bounds.origin(), visible_bounds, view, cx);
-        cx.scene().pop_layer();
-    }
-
-    fn rect_for_text_range(
-        &self,
-        range_utf16: Range<usize>,
-        _: RectF,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> Option<RectF> {
-        self.child.rect_for_text_range(range_utf16, view, cx)
-    }
-
-    fn debug(
-        &self,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> json::Value {
-        json!({"type": "ConstrainedBox", "assigned_constraint": self.constraint.to_json(), "child": self.child.debug(view, cx)})
-    }
-}

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

@@ -1,684 +0,0 @@
-use std::ops::Range;
-
-use crate::{
-    color::Color,
-    geometry::{
-        deserialize_vec2f,
-        rect::RectF,
-        vector::{vec2f, Vector2F},
-    },
-    json::ToJson,
-    platform::CursorStyle,
-    scene::{self, CornerRadii, CursorRegion, Quad},
-    AnyElement, Element, SizeConstraint, ViewContext,
-};
-use schemars::JsonSchema;
-use serde::Deserialize;
-use serde_json::json;
-
-#[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema)]
-pub struct ContainerStyle {
-    #[serde(default)]
-    pub margin: Margin,
-    #[serde(default)]
-    pub padding: Padding,
-    #[serde(rename = "background")]
-    pub background_color: Option<Color>,
-    #[serde(rename = "overlay")]
-    pub overlay_color: Option<Color>,
-    #[serde(default)]
-    pub border: Border,
-    #[serde(default)]
-    #[serde(alias = "corner_radius")]
-    pub corner_radii: CornerRadii,
-    #[serde(default)]
-    pub shadow: Option<Shadow>,
-    #[serde(default)]
-    pub cursor: Option<CursorStyle>,
-}
-
-impl ContainerStyle {
-    pub fn fill(color: Color) -> Self {
-        Self {
-            background_color: Some(color),
-            ..Default::default()
-        }
-    }
-
-    pub fn additional_length(&self) -> f32 {
-        self.padding.left
-            + self.padding.right
-            + self.border.width * 2.
-            + self.margin.left
-            + self.margin.right
-    }
-}
-
-pub struct Container<V> {
-    child: AnyElement<V>,
-    style: ContainerStyle,
-}
-
-impl<V> Container<V> {
-    pub fn new(child: AnyElement<V>) -> Self {
-        Self {
-            child,
-            style: Default::default(),
-        }
-    }
-
-    pub fn with_style(mut self, style: ContainerStyle) -> Self {
-        self.style = style;
-        self
-    }
-
-    pub fn with_margin_top(mut self, margin: f32) -> Self {
-        self.style.margin.top = margin;
-        self
-    }
-
-    pub fn with_margin_bottom(mut self, margin: f32) -> Self {
-        self.style.margin.bottom = margin;
-        self
-    }
-
-    pub fn with_margin_left(mut self, margin: f32) -> Self {
-        self.style.margin.left = margin;
-        self
-    }
-
-    pub fn with_margin_right(mut self, margin: f32) -> Self {
-        self.style.margin.right = margin;
-        self
-    }
-
-    pub fn with_horizontal_padding(mut self, padding: f32) -> Self {
-        self.style.padding.left = padding;
-        self.style.padding.right = padding;
-        self
-    }
-
-    pub fn with_vertical_padding(mut self, padding: f32) -> Self {
-        self.style.padding.top = padding;
-        self.style.padding.bottom = padding;
-        self
-    }
-
-    pub fn with_uniform_padding(mut self, padding: f32) -> Self {
-        self.style.padding = Padding {
-            top: padding,
-            left: padding,
-            bottom: padding,
-            right: padding,
-        };
-        self
-    }
-
-    pub fn with_padding_left(mut self, padding: f32) -> Self {
-        self.style.padding.left = padding;
-        self
-    }
-
-    pub fn with_padding_right(mut self, padding: f32) -> Self {
-        self.style.padding.right = padding;
-        self
-    }
-
-    pub fn with_padding_top(mut self, padding: f32) -> Self {
-        self.style.padding.top = padding;
-        self
-    }
-
-    pub fn with_padding_bottom(mut self, padding: f32) -> Self {
-        self.style.padding.bottom = padding;
-        self
-    }
-
-    pub fn with_background_color(mut self, color: Color) -> Self {
-        self.style.background_color = Some(color);
-        self
-    }
-
-    pub fn with_overlay_color(mut self, color: Color) -> Self {
-        self.style.overlay_color = Some(color);
-        self
-    }
-
-    pub fn with_border(mut self, border: Border) -> Self {
-        self.style.border = border;
-        self
-    }
-
-    pub fn with_corner_radius(mut self, radius: f32) -> Self {
-        self.style.corner_radii.top_left = radius;
-        self.style.corner_radii.top_right = radius;
-        self.style.corner_radii.bottom_right = radius;
-        self.style.corner_radii.bottom_left = radius;
-        self
-    }
-
-    pub fn with_shadow(mut self, offset: Vector2F, blur: f32, color: Color) -> Self {
-        self.style.shadow = Some(Shadow {
-            offset,
-            blur,
-            color,
-        });
-        self
-    }
-
-    pub fn with_cursor(mut self, style: CursorStyle) -> Self {
-        self.style.cursor = Some(style);
-        self
-    }
-
-    fn margin_size(&self) -> Vector2F {
-        vec2f(
-            self.style.margin.left + self.style.margin.right,
-            self.style.margin.top + self.style.margin.bottom,
-        )
-    }
-
-    fn padding_size(&self) -> Vector2F {
-        vec2f(
-            self.style.padding.left + self.style.padding.right,
-            self.style.padding.top + self.style.padding.bottom,
-        )
-    }
-
-    fn border_size(&self) -> Vector2F {
-        let mut x = 0.0;
-        if self.style.border.left {
-            x += self.style.border.width;
-        }
-        if self.style.border.right {
-            x += self.style.border.width;
-        }
-
-        let mut y = 0.0;
-        if self.style.border.top {
-            y += self.style.border.width;
-        }
-        if self.style.border.bottom {
-            y += self.style.border.width;
-        }
-
-        vec2f(x, y)
-    }
-}
-
-#[derive(Copy, Clone, Debug, Default, JsonSchema)]
-pub struct Border {
-    pub color: Color,
-    pub width: f32,
-    pub overlay: bool,
-    pub top: bool,
-    pub bottom: bool,
-    pub left: bool,
-    pub right: bool,
-}
-
-impl Into<scene::Border> for Border {
-    fn into(self) -> scene::Border {
-        scene::Border {
-            color: self.color,
-            left: if self.left { self.width } else { 0.0 },
-            right: if self.right { self.width } else { 0.0 },
-            top: if self.top { self.width } else { 0.0 },
-            bottom: if self.bottom { self.width } else { 0.0 },
-        }
-    }
-}
-
-impl Border {
-    pub fn new(width: f32, color: Color) -> Self {
-        Self {
-            width,
-            color,
-            overlay: false,
-            top: false,
-            left: false,
-            bottom: false,
-            right: false,
-        }
-    }
-
-    pub fn all(width: f32, color: Color) -> Self {
-        Self {
-            width,
-            color,
-            overlay: false,
-            top: true,
-            left: true,
-            bottom: true,
-            right: true,
-        }
-    }
-
-    pub fn top(width: f32, color: Color) -> Self {
-        let mut border = Self::new(width, color);
-        border.top = true;
-        border
-    }
-
-    pub fn left(width: f32, color: Color) -> Self {
-        let mut border = Self::new(width, color);
-        border.left = true;
-        border
-    }
-
-    pub fn bottom(width: f32, color: Color) -> Self {
-        let mut border = Self::new(width, color);
-        border.bottom = true;
-        border
-    }
-
-    pub fn right(width: f32, color: Color) -> Self {
-        let mut border = Self::new(width, color);
-        border.right = true;
-        border
-    }
-
-    pub fn with_sides(mut self, top: bool, left: bool, bottom: bool, right: bool) -> Self {
-        self.top = top;
-        self.left = left;
-        self.bottom = bottom;
-        self.right = right;
-        self
-    }
-
-    pub fn top_width(&self) -> f32 {
-        if self.top {
-            self.width
-        } else {
-            0.0
-        }
-    }
-
-    pub fn left_width(&self) -> f32 {
-        if self.left {
-            self.width
-        } else {
-            0.0
-        }
-    }
-}
-
-impl<'de> Deserialize<'de> for Border {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: serde::Deserializer<'de>,
-    {
-        #[derive(Deserialize)]
-        struct BorderData {
-            pub width: f32,
-            pub color: Color,
-            #[serde(default)]
-            pub overlay: bool,
-            #[serde(default)]
-            pub top: bool,
-            #[serde(default)]
-            pub right: bool,
-            #[serde(default)]
-            pub bottom: bool,
-            #[serde(default)]
-            pub left: bool,
-        }
-
-        let data = BorderData::deserialize(deserializer)?;
-        let mut border = Border {
-            width: data.width,
-            color: data.color,
-            overlay: data.overlay,
-            top: data.top,
-            bottom: data.bottom,
-            left: data.left,
-            right: data.right,
-        };
-        if !border.top && !border.bottom && !border.left && !border.right {
-            border.top = true;
-            border.bottom = true;
-            border.left = true;
-            border.right = true;
-        }
-        Ok(border)
-    }
-}
-
-impl ToJson for Border {
-    fn to_json(&self) -> serde_json::Value {
-        let mut value = json!({});
-        if self.top {
-            value["top"] = json!(self.width);
-        }
-        if self.right {
-            value["right"] = json!(self.width);
-        }
-        if self.bottom {
-            value["bottom"] = json!(self.width);
-        }
-        if self.left {
-            value["left"] = json!(self.width);
-        }
-        value
-    }
-}
-
-impl<V: 'static> Element<V> for Container<V> {
-    type LayoutState = ();
-    type PaintState = ();
-
-    fn layout(
-        &mut self,
-        constraint: SizeConstraint,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> (Vector2F, Self::LayoutState) {
-        let mut size_buffer = self.margin_size() + self.padding_size();
-        if !self.style.border.overlay {
-            size_buffer += self.border_size();
-        }
-        let child_constraint = SizeConstraint {
-            min: (constraint.min - size_buffer).max(Vector2F::zero()),
-            max: (constraint.max - size_buffer).max(Vector2F::zero()),
-        };
-        let child_size = self.child.layout(child_constraint, view, cx);
-        (child_size + size_buffer, ())
-    }
-
-    fn paint(
-        &mut self,
-        bounds: RectF,
-        visible_bounds: RectF,
-        _: &mut Self::LayoutState,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> Self::PaintState {
-        let quad_bounds = RectF::from_points(
-            bounds.origin() + vec2f(self.style.margin.left, self.style.margin.top),
-            bounds.lower_right() - vec2f(self.style.margin.right, self.style.margin.bottom),
-        );
-
-        if let Some(shadow) = self.style.shadow.as_ref() {
-            cx.scene().push_shadow(scene::Shadow {
-                bounds: quad_bounds + shadow.offset,
-                corner_radii: self.style.corner_radii,
-                sigma: shadow.blur,
-                color: shadow.color,
-            });
-        }
-
-        if let Some(hit_bounds) = quad_bounds.intersection(visible_bounds) {
-            if let Some(style) = self.style.cursor {
-                cx.scene().push_cursor_region(CursorRegion {
-                    bounds: hit_bounds,
-                    style,
-                });
-            }
-        }
-
-        let child_origin =
-            quad_bounds.origin() + vec2f(self.style.padding.left, self.style.padding.top);
-
-        if self.style.border.overlay {
-            cx.scene().push_quad(Quad {
-                bounds: quad_bounds,
-                background: self.style.background_color,
-                border: Default::default(),
-                corner_radii: self.style.corner_radii.into(),
-            });
-
-            self.child.paint(child_origin, visible_bounds, view, cx);
-
-            cx.scene().push_layer(None);
-            cx.scene().push_quad(Quad {
-                bounds: quad_bounds,
-                background: self.style.overlay_color,
-                border: self.style.border.into(),
-                corner_radii: self.style.corner_radii.into(),
-            });
-            cx.scene().pop_layer();
-        } else {
-            cx.scene().push_quad(Quad {
-                bounds: quad_bounds,
-                background: self.style.background_color,
-                border: self.style.border.into(),
-                corner_radii: self.style.corner_radii.into(),
-            });
-
-            let child_origin = child_origin
-                + vec2f(
-                    self.style.border.left_width(),
-                    self.style.border.top_width(),
-                );
-            self.child.paint(child_origin, visible_bounds, view, cx);
-
-            if self.style.overlay_color.is_some() {
-                cx.scene().push_layer(None);
-                cx.scene().push_quad(Quad {
-                    bounds: quad_bounds,
-                    background: self.style.overlay_color,
-                    border: Default::default(),
-                    corner_radii: self.style.corner_radii.into(),
-                });
-                cx.scene().pop_layer();
-            }
-        }
-    }
-
-    fn rect_for_text_range(
-        &self,
-        range_utf16: Range<usize>,
-        _: RectF,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> Option<RectF> {
-        self.child.rect_for_text_range(range_utf16, view, cx)
-    }
-
-    fn debug(
-        &self,
-        bounds: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> serde_json::Value {
-        json!({
-            "type": "Container",
-            "bounds": bounds.to_json(),
-            "details": self.style.to_json(),
-            "child": self.child.debug(view, cx),
-        })
-    }
-}
-
-impl ToJson for ContainerStyle {
-    fn to_json(&self) -> serde_json::Value {
-        json!({
-            "margin": self.margin.to_json(),
-            "padding": self.padding.to_json(),
-            "background_color": self.background_color.to_json(),
-            "border": self.border.to_json(),
-            "corner_radius": self.corner_radii,
-            "shadow": self.shadow.to_json(),
-        })
-    }
-}
-
-#[derive(Clone, Copy, Debug, Default, JsonSchema)]
-pub struct Margin {
-    pub top: f32,
-    pub bottom: f32,
-    pub left: f32,
-    pub right: f32,
-}
-
-impl ToJson for Margin {
-    fn to_json(&self) -> serde_json::Value {
-        let mut value = json!({});
-        if self.top > 0. {
-            value["top"] = json!(self.top);
-        }
-        if self.right > 0. {
-            value["right"] = json!(self.right);
-        }
-        if self.bottom > 0. {
-            value["bottom"] = json!(self.bottom);
-        }
-        if self.left > 0. {
-            value["left"] = json!(self.left);
-        }
-        value
-    }
-}
-
-#[derive(Clone, Copy, Debug, Default, JsonSchema)]
-pub struct Padding {
-    pub top: f32,
-    pub left: f32,
-    pub bottom: f32,
-    pub right: f32,
-}
-
-impl Padding {
-    pub fn horizontal(padding: f32) -> Self {
-        Self {
-            left: padding,
-            right: padding,
-            ..Default::default()
-        }
-    }
-
-    pub fn vertical(padding: f32) -> Self {
-        Self {
-            top: padding,
-            bottom: padding,
-            ..Default::default()
-        }
-    }
-}
-
-impl<'de> Deserialize<'de> for Padding {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: serde::Deserializer<'de>,
-    {
-        let spacing = Spacing::deserialize(deserializer)?;
-        Ok(match spacing {
-            Spacing::Uniform(size) => Padding {
-                top: size,
-                left: size,
-                bottom: size,
-                right: size,
-            },
-            Spacing::Specific {
-                top,
-                left,
-                bottom,
-                right,
-            } => Padding {
-                top,
-                left,
-                bottom,
-                right,
-            },
-        })
-    }
-}
-
-impl<'de> Deserialize<'de> for Margin {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: serde::Deserializer<'de>,
-    {
-        let spacing = Spacing::deserialize(deserializer)?;
-        Ok(match spacing {
-            Spacing::Uniform(size) => Margin {
-                top: size,
-                left: size,
-                bottom: size,
-                right: size,
-            },
-            Spacing::Specific {
-                top,
-                left,
-                bottom,
-                right,
-            } => Margin {
-                top,
-                left,
-                bottom,
-                right,
-            },
-        })
-    }
-}
-#[derive(Deserialize)]
-#[serde(untagged)]
-enum Spacing {
-    Uniform(f32),
-    Specific {
-        #[serde(default)]
-        top: f32,
-        #[serde(default)]
-        left: f32,
-        #[serde(default)]
-        bottom: f32,
-        #[serde(default)]
-        right: f32,
-    },
-}
-
-impl Padding {
-    pub fn uniform(padding: f32) -> Self {
-        Self {
-            top: padding,
-            left: padding,
-            bottom: padding,
-            right: padding,
-        }
-    }
-}
-
-impl ToJson for Padding {
-    fn to_json(&self) -> serde_json::Value {
-        let mut value = json!({});
-        if self.top > 0. {
-            value["top"] = json!(self.top);
-        }
-        if self.right > 0. {
-            value["right"] = json!(self.right);
-        }
-        if self.bottom > 0. {
-            value["bottom"] = json!(self.bottom);
-        }
-        if self.left > 0. {
-            value["left"] = json!(self.left);
-        }
-        value
-    }
-}
-
-#[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema)]
-pub struct Shadow {
-    #[serde(default, deserialize_with = "deserialize_vec2f")]
-    #[schemars(with = "Vec::<f32>")]
-    offset: Vector2F,
-    #[serde(default)]
-    blur: f32,
-    #[serde(default)]
-    color: Color,
-}
-
-impl ToJson for Shadow {
-    fn to_json(&self) -> serde_json::Value {
-        json!({
-            "offset": self.offset.to_json(),
-            "blur": self.blur,
-            "color": self.color.to_json()
-        })
-    }
-}

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

@@ -1,89 +0,0 @@
-use std::ops::Range;
-
-use crate::{
-    geometry::{
-        rect::RectF,
-        vector::{vec2f, Vector2F},
-    },
-    json::{json, ToJson},
-    ViewContext,
-};
-use crate::{Element, SizeConstraint};
-
-#[derive(Default)]
-pub struct Empty {
-    collapsed: bool,
-}
-
-impl Empty {
-    pub fn new() -> Self {
-        Self::default()
-    }
-
-    pub fn collapsed(mut self) -> Self {
-        self.collapsed = true;
-        self
-    }
-}
-
-impl<V: 'static> Element<V> for Empty {
-    type LayoutState = ();
-    type PaintState = ();
-
-    fn layout(
-        &mut self,
-        constraint: SizeConstraint,
-        _: &mut V,
-        _: &mut ViewContext<V>,
-    ) -> (Vector2F, Self::LayoutState) {
-        let x = if constraint.max.x().is_finite() && !self.collapsed {
-            constraint.max.x()
-        } else {
-            constraint.min.x()
-        };
-        let y = if constraint.max.y().is_finite() && !self.collapsed {
-            constraint.max.y()
-        } else {
-            constraint.min.y()
-        };
-
-        (vec2f(x, y), ())
-    }
-
-    fn paint(
-        &mut self,
-        _: RectF,
-        _: RectF,
-        _: &mut Self::LayoutState,
-        _: &mut V,
-        _: &mut ViewContext<V>,
-    ) -> Self::PaintState {
-    }
-
-    fn rect_for_text_range(
-        &self,
-        _: Range<usize>,
-        _: RectF,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        _: &V,
-        _: &ViewContext<V>,
-    ) -> Option<RectF> {
-        None
-    }
-
-    fn debug(
-        &self,
-        bounds: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        _: &V,
-        _: &ViewContext<V>,
-    ) -> serde_json::Value {
-        json!({
-            "type": "Empty",
-            "bounds": bounds.to_json(),
-        })
-    }
-}

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

@@ -1,96 +0,0 @@
-use std::ops::Range;
-
-use crate::{
-    geometry::{rect::RectF, vector::Vector2F},
-    json, AnyElement, Element, SizeConstraint, ViewContext,
-};
-use serde_json::json;
-
-pub struct Expanded<V> {
-    child: AnyElement<V>,
-    full_width: bool,
-    full_height: bool,
-}
-
-impl<V: 'static> Expanded<V> {
-    pub fn new(child: impl Element<V>) -> Self {
-        Self {
-            child: child.into_any(),
-            full_width: true,
-            full_height: true,
-        }
-    }
-
-    pub fn full_width(mut self) -> Self {
-        self.full_width = true;
-        self.full_height = false;
-        self
-    }
-
-    pub fn full_height(mut self) -> Self {
-        self.full_width = false;
-        self.full_height = true;
-        self
-    }
-}
-
-impl<V: 'static> Element<V> for Expanded<V> {
-    type LayoutState = ();
-    type PaintState = ();
-
-    fn layout(
-        &mut self,
-        mut constraint: SizeConstraint,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> (Vector2F, Self::LayoutState) {
-        if self.full_width {
-            constraint.min.set_x(constraint.max.x());
-        }
-        if self.full_height {
-            constraint.min.set_y(constraint.max.y());
-        }
-        let size = self.child.layout(constraint, view, cx);
-        (size, ())
-    }
-
-    fn paint(
-        &mut self,
-        bounds: RectF,
-        visible_bounds: RectF,
-        _: &mut Self::LayoutState,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> Self::PaintState {
-        self.child.paint(bounds.origin(), visible_bounds, view, cx);
-    }
-
-    fn rect_for_text_range(
-        &self,
-        range_utf16: Range<usize>,
-        _: RectF,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> Option<RectF> {
-        self.child.rect_for_text_range(range_utf16, view, cx)
-    }
-
-    fn debug(
-        &self,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> json::Value {
-        json!({
-            "type": "Expanded",
-            "full_width": self.full_width,
-            "full_height": self.full_height,
-            "child": self.child.debug(view, cx)
-        })
-    }
-}

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

@@ -1,512 +0,0 @@
-use std::{any::Any, cell::Cell, f32::INFINITY, ops::Range, rc::Rc};
-
-use crate::{
-    json::{self, ToJson, Value},
-    AnyElement, Axis, Element, ElementStateHandle, SizeConstraint, TypeTag, Vector2FExt,
-    ViewContext,
-};
-use pathfinder_geometry::{
-    rect::RectF,
-    vector::{vec2f, Vector2F},
-};
-use serde_json::json;
-
-struct ScrollState {
-    scroll_to: Cell<Option<usize>>,
-    scroll_position: Cell<f32>,
-    type_tag: TypeTag,
-}
-
-pub struct Flex<V> {
-    axis: Axis,
-    children: Vec<AnyElement<V>>,
-    scroll_state: Option<(ElementStateHandle<Rc<ScrollState>>, usize)>,
-    child_alignment: f32,
-    spacing: f32,
-}
-
-impl<V: 'static> Flex<V> {
-    pub fn new(axis: Axis) -> Self {
-        Self {
-            axis,
-            children: Default::default(),
-            scroll_state: None,
-            child_alignment: -1.,
-            spacing: 0.,
-        }
-    }
-
-    pub fn row() -> Self {
-        Self::new(Axis::Horizontal)
-    }
-
-    pub fn column() -> Self {
-        Self::new(Axis::Vertical)
-    }
-
-    /// Render children centered relative to the cross-axis of the parent flex.
-    ///
-    /// If this is a flex row, children will be centered vertically. If this is a
-    /// flex column, children will be centered horizontally.
-    pub fn align_children_center(mut self) -> Self {
-        self.child_alignment = 0.;
-        self
-    }
-
-    pub fn with_spacing(mut self, spacing: f32) -> Self {
-        self.spacing = spacing;
-        self
-    }
-
-    pub fn scrollable<Tag>(
-        mut self,
-        element_id: usize,
-        scroll_to: Option<usize>,
-        cx: &mut ViewContext<V>,
-    ) -> Self
-    where
-        Tag: 'static,
-    {
-        // Don't assume that this initialization is what scroll_state really is in other panes:
-        // `element_state` is shared and there could be init races.
-        let scroll_state = cx.element_state::<Tag, Rc<ScrollState>>(
-            element_id,
-            Rc::new(ScrollState {
-                type_tag: TypeTag::new::<Tag>(),
-                scroll_to: Default::default(),
-                scroll_position: Default::default(),
-            }),
-        );
-        // Set scroll_to separately, because the default state is already picked as `None` by other panes
-        // by the time we start setting it here, hence update all others' state too.
-        scroll_state.update(cx, |this, _| {
-            this.scroll_to.set(scroll_to);
-        });
-        self.scroll_state = Some((scroll_state, cx.handle().id()));
-        self
-    }
-
-    pub fn is_empty(&self) -> bool {
-        self.children.is_empty()
-    }
-
-    fn layout_flex_children(
-        &mut self,
-        layout_expanded: bool,
-        constraint: SizeConstraint,
-        remaining_space: &mut f32,
-        remaining_flex: &mut f32,
-        cross_axis_max: &mut f32,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) {
-        let cross_axis = self.axis.invert();
-        for child in self.children.iter_mut() {
-            if let Some(metadata) = child.metadata::<FlexParentData>() {
-                if let Some((flex, expanded)) = metadata.flex {
-                    if expanded != layout_expanded {
-                        continue;
-                    }
-
-                    let child_max = if *remaining_flex == 0.0 {
-                        *remaining_space
-                    } else {
-                        let space_per_flex = *remaining_space / *remaining_flex;
-                        space_per_flex * flex
-                    };
-                    let child_min = if expanded { child_max } else { 0. };
-                    let child_constraint = match self.axis {
-                        Axis::Horizontal => SizeConstraint::new(
-                            vec2f(child_min, constraint.min.y()),
-                            vec2f(child_max, constraint.max.y()),
-                        ),
-                        Axis::Vertical => SizeConstraint::new(
-                            vec2f(constraint.min.x(), child_min),
-                            vec2f(constraint.max.x(), child_max),
-                        ),
-                    };
-                    let child_size = child.layout(child_constraint, view, cx);
-                    *remaining_space -= child_size.along(self.axis);
-                    *remaining_flex -= flex;
-                    *cross_axis_max = cross_axis_max.max(child_size.along(cross_axis));
-                }
-            }
-        }
-    }
-}
-
-impl<V> Extend<AnyElement<V>> for Flex<V> {
-    fn extend<T: IntoIterator<Item = AnyElement<V>>>(&mut self, children: T) {
-        self.children.extend(children);
-    }
-}
-
-impl<V: 'static> Element<V> for Flex<V> {
-    type LayoutState = f32;
-    type PaintState = ();
-
-    fn layout(
-        &mut self,
-        constraint: SizeConstraint,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> (Vector2F, Self::LayoutState) {
-        let mut total_flex = None;
-        let mut fixed_space = self.children.len().saturating_sub(1) as f32 * self.spacing;
-        let mut contains_float = false;
-
-        let cross_axis = self.axis.invert();
-        let mut cross_axis_max: f32 = 0.0;
-        for child in self.children.iter_mut() {
-            let metadata = child.metadata::<FlexParentData>();
-            contains_float |= metadata.map_or(false, |metadata| metadata.float);
-
-            if let Some(flex) = metadata.and_then(|metadata| metadata.flex.map(|(flex, _)| flex)) {
-                *total_flex.get_or_insert(0.) += flex;
-            } else {
-                let child_constraint = match self.axis {
-                    Axis::Horizontal => SizeConstraint::new(
-                        vec2f(0.0, constraint.min.y()),
-                        vec2f(INFINITY, constraint.max.y()),
-                    ),
-                    Axis::Vertical => SizeConstraint::new(
-                        vec2f(constraint.min.x(), 0.0),
-                        vec2f(constraint.max.x(), INFINITY),
-                    ),
-                };
-                let size = child.layout(child_constraint, view, cx);
-                fixed_space += size.along(self.axis);
-                cross_axis_max = cross_axis_max.max(size.along(cross_axis));
-            }
-        }
-
-        let mut remaining_space = constraint.max_along(self.axis) - fixed_space;
-        let mut size = if let Some(mut remaining_flex) = total_flex {
-            if remaining_space.is_infinite() {
-                panic!("flex contains flexible children but has an infinite constraint along the flex axis");
-            }
-
-            self.layout_flex_children(
-                false,
-                constraint,
-                &mut remaining_space,
-                &mut remaining_flex,
-                &mut cross_axis_max,
-                view,
-                cx,
-            );
-            self.layout_flex_children(
-                true,
-                constraint,
-                &mut remaining_space,
-                &mut remaining_flex,
-                &mut cross_axis_max,
-                view,
-                cx,
-            );
-
-            match self.axis {
-                Axis::Horizontal => vec2f(constraint.max.x() - remaining_space, cross_axis_max),
-                Axis::Vertical => vec2f(cross_axis_max, constraint.max.y() - remaining_space),
-            }
-        } else {
-            match self.axis {
-                Axis::Horizontal => vec2f(fixed_space, cross_axis_max),
-                Axis::Vertical => vec2f(cross_axis_max, fixed_space),
-            }
-        };
-
-        if contains_float {
-            match self.axis {
-                Axis::Horizontal => size.set_x(size.x().max(constraint.max.x())),
-                Axis::Vertical => size.set_y(size.y().max(constraint.max.y())),
-            }
-        }
-
-        if constraint.min.x().is_finite() {
-            size.set_x(size.x().max(constraint.min.x()));
-        }
-        if constraint.min.y().is_finite() {
-            size.set_y(size.y().max(constraint.min.y()));
-        }
-
-        if size.x() > constraint.max.x() {
-            size.set_x(constraint.max.x());
-        }
-        if size.y() > constraint.max.y() {
-            size.set_y(constraint.max.y());
-        }
-
-        if let Some(scroll_state) = self.scroll_state.as_ref() {
-            scroll_state.0.update(cx, |scroll_state, _| {
-                if let Some(scroll_to) = scroll_state.scroll_to.take() {
-                    let visible_start = scroll_state.scroll_position.get();
-                    let visible_end = visible_start + size.along(self.axis);
-                    if let Some(child) = self.children.get(scroll_to) {
-                        let child_start: f32 = self.children[..scroll_to]
-                            .iter()
-                            .map(|c| c.size().along(self.axis))
-                            .sum();
-                        let child_end = child_start + child.size().along(self.axis);
-                        if child_start < visible_start {
-                            scroll_state.scroll_position.set(child_start);
-                        } else if child_end > visible_end {
-                            scroll_state
-                                .scroll_position
-                                .set(child_end - size.along(self.axis));
-                        }
-                    }
-                }
-
-                scroll_state.scroll_position.set(
-                    scroll_state
-                        .scroll_position
-                        .get()
-                        .min(-remaining_space)
-                        .max(0.),
-                );
-            });
-        }
-
-        (size, remaining_space)
-    }
-
-    fn paint(
-        &mut self,
-        bounds: RectF,
-        visible_bounds: RectF,
-        remaining_space: &mut Self::LayoutState,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> Self::PaintState {
-        let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
-
-        let mut remaining_space = *remaining_space;
-        let overflowing = remaining_space < 0.;
-        if overflowing {
-            cx.scene().push_layer(Some(visible_bounds));
-        }
-
-        if let Some((scroll_state, id)) = &self.scroll_state {
-            let scroll_state = scroll_state.read(cx).clone();
-            cx.scene().push_mouse_region(
-                crate::MouseRegion::from_handlers(
-                    scroll_state.type_tag,
-                    *id,
-                    0,
-                    bounds,
-                    Default::default(),
-                )
-                .on_scroll({
-                    let axis = self.axis;
-                    move |e, _: &mut V, cx| {
-                        if remaining_space < 0. {
-                            let scroll_delta = e.delta.raw();
-
-                            let mut delta = match axis {
-                                Axis::Horizontal => {
-                                    if scroll_delta.x().abs() >= scroll_delta.y().abs() {
-                                        scroll_delta.x()
-                                    } else {
-                                        scroll_delta.y()
-                                    }
-                                }
-                                Axis::Vertical => scroll_delta.y(),
-                            };
-                            if !e.delta.precise() {
-                                delta *= 20.;
-                            }
-
-                            scroll_state
-                                .scroll_position
-                                .set(scroll_state.scroll_position.get() - delta);
-
-                            cx.notify();
-                        } else {
-                            cx.propagate_event();
-                        }
-                    }
-                })
-                .on_move(|_, _: &mut V, _| { /* Capture move events */ }),
-            )
-        }
-
-        let mut child_origin = bounds.origin();
-        if let Some(scroll_state) = self.scroll_state.as_ref() {
-            let scroll_position = scroll_state.0.read(cx).scroll_position.get();
-            match self.axis {
-                Axis::Horizontal => child_origin.set_x(child_origin.x() - scroll_position),
-                Axis::Vertical => child_origin.set_y(child_origin.y() - scroll_position),
-            }
-        }
-
-        for child in self.children.iter_mut() {
-            if remaining_space > 0. {
-                if let Some(metadata) = child.metadata::<FlexParentData>() {
-                    if metadata.float {
-                        match self.axis {
-                            Axis::Horizontal => child_origin += vec2f(remaining_space, 0.0),
-                            Axis::Vertical => child_origin += vec2f(0.0, remaining_space),
-                        }
-                        remaining_space = 0.;
-                    }
-                }
-            }
-
-            // We use the child_alignment f32 to determine a point along the cross axis of the
-            // overall flex element and each child. We then align these points. So 0 would center
-            // each child relative to the overall height/width of the flex. -1 puts children at
-            // the start. 1 puts children at the end.
-            let aligned_child_origin = {
-                let cross_axis = self.axis.invert();
-                let my_center = bounds.size().along(cross_axis) / 2.;
-                let my_target = my_center + my_center * self.child_alignment;
-
-                let child_center = child.size().along(cross_axis) / 2.;
-                let child_target = child_center + child_center * self.child_alignment;
-
-                let mut aligned_child_origin = child_origin;
-                match self.axis {
-                    Axis::Horizontal => aligned_child_origin
-                        .set_y(aligned_child_origin.y() - (child_target - my_target)),
-                    Axis::Vertical => aligned_child_origin
-                        .set_x(aligned_child_origin.x() - (child_target - my_target)),
-                }
-
-                aligned_child_origin
-            };
-
-            child.paint(aligned_child_origin, visible_bounds, view, cx);
-
-            match self.axis {
-                Axis::Horizontal => child_origin += vec2f(child.size().x() + self.spacing, 0.0),
-                Axis::Vertical => child_origin += vec2f(0.0, child.size().y() + self.spacing),
-            }
-        }
-
-        if overflowing {
-            cx.scene().pop_layer();
-        }
-    }
-
-    fn rect_for_text_range(
-        &self,
-        range_utf16: Range<usize>,
-        _: RectF,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> Option<RectF> {
-        self.children
-            .iter()
-            .find_map(|child| child.rect_for_text_range(range_utf16.clone(), view, cx))
-    }
-
-    fn debug(
-        &self,
-        bounds: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> json::Value {
-        json!({
-            "type": "Flex",
-            "bounds": bounds.to_json(),
-            "axis": self.axis.to_json(),
-            "children": self.children.iter().map(|child| child.debug(view, cx)).collect::<Vec<json::Value>>()
-        })
-    }
-}
-
-struct FlexParentData {
-    flex: Option<(f32, bool)>,
-    float: bool,
-}
-
-pub struct FlexItem<V> {
-    metadata: FlexParentData,
-    child: AnyElement<V>,
-}
-
-impl<V: 'static> FlexItem<V> {
-    pub fn new(child: impl Element<V>) -> Self {
-        FlexItem {
-            metadata: FlexParentData {
-                flex: None,
-                float: false,
-            },
-            child: child.into_any(),
-        }
-    }
-
-    pub fn flex(mut self, flex: f32, expanded: bool) -> Self {
-        self.metadata.flex = Some((flex, expanded));
-        self
-    }
-
-    pub fn float(mut self) -> Self {
-        self.metadata.float = true;
-        self
-    }
-}
-
-impl<V: 'static> Element<V> for FlexItem<V> {
-    type LayoutState = ();
-    type PaintState = ();
-
-    fn layout(
-        &mut self,
-        constraint: SizeConstraint,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> (Vector2F, Self::LayoutState) {
-        let size = self.child.layout(constraint, view, cx);
-        (size, ())
-    }
-
-    fn paint(
-        &mut self,
-        bounds: RectF,
-        visible_bounds: RectF,
-        _: &mut Self::LayoutState,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> Self::PaintState {
-        self.child.paint(bounds.origin(), visible_bounds, view, cx)
-    }
-
-    fn rect_for_text_range(
-        &self,
-        range_utf16: Range<usize>,
-        _: RectF,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> Option<RectF> {
-        self.child.rect_for_text_range(range_utf16, view, cx)
-    }
-
-    fn metadata(&self) -> Option<&dyn Any> {
-        Some(&self.metadata)
-    }
-
-    fn debug(
-        &self,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> Value {
-        json!({
-            "type": "Flexible",
-            "flex": self.metadata.flex,
-            "child": self.child.debug(view, cx)
-        })
-    }
-}

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

@@ -1,85 +0,0 @@
-use std::ops::Range;
-
-use crate::{
-    geometry::{rect::RectF, vector::Vector2F},
-    json::json,
-    AnyElement, Element, SizeConstraint, ViewContext,
-};
-
-pub struct Hook<V> {
-    child: AnyElement<V>,
-    after_layout: Option<Box<dyn FnMut(Vector2F, &mut ViewContext<V>)>>,
-}
-
-impl<V: 'static> Hook<V> {
-    pub fn new(child: impl Element<V>) -> Self {
-        Self {
-            child: child.into_any(),
-            after_layout: None,
-        }
-    }
-
-    pub fn on_after_layout(
-        mut self,
-        f: impl 'static + FnMut(Vector2F, &mut ViewContext<V>),
-    ) -> Self {
-        self.after_layout = Some(Box::new(f));
-        self
-    }
-}
-
-impl<V: 'static> Element<V> for Hook<V> {
-    type LayoutState = ();
-    type PaintState = ();
-
-    fn layout(
-        &mut self,
-        constraint: SizeConstraint,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> (Vector2F, Self::LayoutState) {
-        let size = self.child.layout(constraint, view, cx);
-        if let Some(handler) = self.after_layout.as_mut() {
-            handler(size, cx);
-        }
-        (size, ())
-    }
-
-    fn paint(
-        &mut self,
-        bounds: RectF,
-        visible_bounds: RectF,
-        _: &mut Self::LayoutState,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) {
-        self.child.paint(bounds.origin(), visible_bounds, view, cx);
-    }
-
-    fn rect_for_text_range(
-        &self,
-        range_utf16: Range<usize>,
-        _: RectF,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> Option<RectF> {
-        self.child.rect_for_text_range(range_utf16, view, cx)
-    }
-
-    fn debug(
-        &self,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> serde_json::Value {
-        json!({
-            "type": "Hooks",
-            "child": self.child.debug(view, cx),
-        })
-    }
-}

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

@@ -1,137 +0,0 @@
-use super::{constrain_size_preserving_aspect_ratio, Border};
-use crate::{
-    geometry::{
-        rect::RectF,
-        vector::{vec2f, Vector2F},
-    },
-    json::{json, ToJson},
-    scene, Element, ImageData, SizeConstraint, ViewContext,
-};
-use schemars::JsonSchema;
-use serde::Deserialize;
-use std::{ops::Range, sync::Arc};
-
-enum ImageSource {
-    Path(&'static str),
-    Data(Arc<ImageData>),
-}
-
-pub struct Image {
-    source: ImageSource,
-    style: ImageStyle,
-}
-
-#[derive(Copy, Clone, Default, Deserialize, JsonSchema)]
-pub struct ImageStyle {
-    #[serde(default)]
-    pub border: Border,
-    #[serde(default)]
-    pub corner_radius: f32,
-    #[serde(default)]
-    pub height: Option<f32>,
-    #[serde(default)]
-    pub width: Option<f32>,
-    #[serde(default)]
-    pub grayscale: bool,
-}
-
-impl Image {
-    pub fn new(asset_path: &'static str) -> Self {
-        Self {
-            source: ImageSource::Path(asset_path),
-            style: Default::default(),
-        }
-    }
-
-    pub fn from_data(data: Arc<ImageData>) -> Self {
-        Self {
-            source: ImageSource::Data(data),
-            style: Default::default(),
-        }
-    }
-
-    pub fn with_style(mut self, style: ImageStyle) -> Self {
-        self.style = style;
-        self
-    }
-}
-
-impl<V: 'static> Element<V> for Image {
-    type LayoutState = Option<Arc<ImageData>>;
-    type PaintState = ();
-
-    fn layout(
-        &mut self,
-        constraint: SizeConstraint,
-        _: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> (Vector2F, Self::LayoutState) {
-        let data = match &self.source {
-            ImageSource::Path(path) => match cx.asset_cache.png(path) {
-                Ok(data) => data,
-                Err(error) => {
-                    log::error!("could not load image: {}", error);
-                    return (Vector2F::zero(), None);
-                }
-            },
-            ImageSource::Data(data) => data.clone(),
-        };
-
-        let desired_size = vec2f(
-            self.style.width.unwrap_or_else(|| constraint.max.x()),
-            self.style.height.unwrap_or_else(|| constraint.max.y()),
-        );
-        let size = constrain_size_preserving_aspect_ratio(
-            constraint.constrain(desired_size),
-            data.size().to_f32(),
-        );
-
-        (size, Some(data))
-    }
-
-    fn paint(
-        &mut self,
-        bounds: RectF,
-        _: RectF,
-        layout: &mut Self::LayoutState,
-        _: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> Self::PaintState {
-        if let Some(data) = layout {
-            cx.scene().push_image(scene::Image {
-                bounds,
-                border: self.style.border.into(),
-                corner_radii: self.style.corner_radius.into(),
-                grayscale: self.style.grayscale,
-                data: data.clone(),
-            });
-        }
-    }
-
-    fn rect_for_text_range(
-        &self,
-        _: Range<usize>,
-        _: RectF,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        _: &V,
-        _: &ViewContext<V>,
-    ) -> Option<RectF> {
-        None
-    }
-
-    fn debug(
-        &self,
-        bounds: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        _: &V,
-        _: &ViewContext<V>,
-    ) -> serde_json::Value {
-        json!({
-            "type": "Image",
-            "bounds": bounds.to_json(),
-        })
-    }
-}

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

@@ -1,100 +0,0 @@
-use crate::{
-    elements::*,
-    fonts::TextStyle,
-    geometry::{rect::RectF, vector::Vector2F},
-    Action, AnyElement, SizeConstraint,
-};
-use serde_json::json;
-
-use super::ContainerStyle;
-
-pub struct KeystrokeLabel {
-    action: Box<dyn Action>,
-    container_style: ContainerStyle,
-    text_style: TextStyle,
-    view_id: usize,
-}
-
-impl KeystrokeLabel {
-    pub fn new(
-        view_id: usize,
-        action: Box<dyn Action>,
-        container_style: ContainerStyle,
-        text_style: TextStyle,
-    ) -> Self {
-        Self {
-            view_id,
-            action,
-            container_style,
-            text_style,
-        }
-    }
-}
-
-impl<V: 'static> Element<V> for KeystrokeLabel {
-    type LayoutState = AnyElement<V>;
-    type PaintState = ();
-
-    fn layout(
-        &mut self,
-        constraint: SizeConstraint,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> (Vector2F, AnyElement<V>) {
-        let mut element = if let Some(keystrokes) =
-            cx.keystrokes_for_action(self.view_id, self.action.as_ref())
-        {
-            Flex::row()
-                .with_children(keystrokes.iter().map(|keystroke| {
-                    Label::new(keystroke.to_string(), self.text_style.clone())
-                        .contained()
-                        .with_style(self.container_style)
-                }))
-                .into_any()
-        } else {
-            Empty::new().collapsed().into_any()
-        };
-
-        let size = element.layout(constraint, view, cx);
-        (size, element)
-    }
-
-    fn paint(
-        &mut self,
-        bounds: RectF,
-        visible_bounds: RectF,
-        element: &mut AnyElement<V>,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) {
-        element.paint(bounds.origin(), visible_bounds, view, cx);
-    }
-
-    fn rect_for_text_range(
-        &self,
-        _: Range<usize>,
-        _: RectF,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        _: &V,
-        _: &ViewContext<V>,
-    ) -> Option<RectF> {
-        None
-    }
-
-    fn debug(
-        &self,
-        _: RectF,
-        element: &AnyElement<V>,
-        _: &(),
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> serde_json::Value {
-        json!({
-            "type": "KeystrokeLabel",
-            "action": self.action.name(),
-            "child": element.debug(view, cx)
-        })
-    }
-}

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

@@ -1,280 +0,0 @@
-use std::{borrow::Cow, ops::Range};
-
-use crate::{
-    fonts::TextStyle,
-    geometry::{
-        rect::RectF,
-        vector::{vec2f, Vector2F},
-    },
-    json::{ToJson, Value},
-    text_layout::{Line, RunStyle},
-    Element, SizeConstraint, ViewContext,
-};
-use schemars::JsonSchema;
-use serde::Deserialize;
-use serde_json::json;
-use smallvec::{smallvec, SmallVec};
-
-pub struct Label {
-    text: Cow<'static, str>,
-    style: LabelStyle,
-    highlight_indices: Vec<usize>,
-}
-
-#[derive(Clone, Debug, Deserialize, Default, JsonSchema)]
-pub struct LabelStyle {
-    pub text: TextStyle,
-    pub highlight_text: Option<TextStyle>,
-}
-
-impl From<TextStyle> for LabelStyle {
-    fn from(text: TextStyle) -> Self {
-        LabelStyle {
-            text,
-            highlight_text: None,
-        }
-    }
-}
-
-impl LabelStyle {
-    pub fn with_font_size(mut self, font_size: f32) -> Self {
-        self.text.font_size = font_size;
-        self
-    }
-}
-
-impl Label {
-    pub fn new<I: Into<Cow<'static, str>>>(text: I, style: impl Into<LabelStyle>) -> Self {
-        Self {
-            text: text.into(),
-            highlight_indices: Default::default(),
-            style: style.into(),
-        }
-    }
-
-    pub fn with_highlights(mut self, indices: Vec<usize>) -> Self {
-        self.highlight_indices = indices;
-        self
-    }
-
-    fn compute_runs(&self) -> SmallVec<[(usize, RunStyle); 8]> {
-        let font_id = self.style.text.font_id;
-        if self.highlight_indices.is_empty() {
-            return smallvec![(
-                self.text.len(),
-                RunStyle {
-                    font_id,
-                    color: self.style.text.color,
-                    underline: self.style.text.underline,
-                }
-            )];
-        }
-
-        let highlight_font_id = self
-            .style
-            .highlight_text
-            .as_ref()
-            .map_or(font_id, |style| style.font_id);
-
-        let mut highlight_indices = self.highlight_indices.iter().copied().peekable();
-        let mut runs = SmallVec::new();
-        let highlight_style = self
-            .style
-            .highlight_text
-            .as_ref()
-            .unwrap_or(&self.style.text);
-
-        for (char_ix, c) in self.text.char_indices() {
-            let mut font_id = font_id;
-            let mut color = self.style.text.color;
-            let mut underline = self.style.text.underline;
-            if let Some(highlight_ix) = highlight_indices.peek() {
-                if char_ix == *highlight_ix {
-                    font_id = highlight_font_id;
-                    color = highlight_style.color;
-                    underline = highlight_style.underline;
-                    highlight_indices.next();
-                }
-            }
-
-            let last_run: Option<&mut (usize, RunStyle)> = runs.last_mut();
-            let push_new_run = if let Some((last_len, last_style)) = last_run {
-                if font_id == last_style.font_id
-                    && color == last_style.color
-                    && underline == last_style.underline
-                {
-                    *last_len += c.len_utf8();
-                    false
-                } else {
-                    true
-                }
-            } else {
-                true
-            };
-
-            if push_new_run {
-                runs.push((
-                    c.len_utf8(),
-                    RunStyle {
-                        font_id,
-                        color,
-                        underline,
-                    },
-                ));
-            }
-        }
-
-        runs
-    }
-}
-
-impl<V: 'static> Element<V> for Label {
-    type LayoutState = Line;
-    type PaintState = ();
-
-    fn layout(
-        &mut self,
-        constraint: SizeConstraint,
-        _: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> (Vector2F, Self::LayoutState) {
-        let runs = self.compute_runs();
-        let line = cx.text_layout_cache().layout_str(
-            &self.text,
-            self.style.text.font_size,
-            runs.as_slice(),
-        );
-
-        let size = vec2f(
-            line.width()
-                .ceil()
-                .max(constraint.min.x())
-                .min(constraint.max.x()),
-            cx.font_cache.line_height(self.style.text.font_size),
-        );
-
-        (size, line)
-    }
-
-    fn paint(
-        &mut self,
-        bounds: RectF,
-        visible_bounds: RectF,
-        line: &mut Self::LayoutState,
-        _: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> Self::PaintState {
-        let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
-        line.paint(bounds.origin(), visible_bounds, bounds.size().y(), cx)
-    }
-
-    fn rect_for_text_range(
-        &self,
-        _: Range<usize>,
-        _: RectF,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        _: &V,
-        _: &ViewContext<V>,
-    ) -> Option<RectF> {
-        None
-    }
-
-    fn debug(
-        &self,
-        bounds: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        _: &V,
-        _: &ViewContext<V>,
-    ) -> Value {
-        json!({
-            "type": "Label",
-            "bounds": bounds.to_json(),
-            "text": &self.text,
-            "highlight_indices": self.highlight_indices,
-            "style": self.style.to_json(),
-        })
-    }
-}
-
-impl ToJson for LabelStyle {
-    fn to_json(&self) -> Value {
-        json!({
-            "text": self.text.to_json(),
-            "highlight_text": self.highlight_text
-                .as_ref()
-                .map_or(serde_json::Value::Null, |style| style.to_json())
-        })
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::color::Color;
-    use crate::fonts::{Properties as FontProperties, Weight};
-
-    #[crate::test(self)]
-    fn test_layout_label_with_highlights(cx: &mut crate::AppContext) {
-        let default_style = TextStyle::new(
-            "Menlo",
-            12.,
-            Default::default(),
-            Default::default(),
-            Default::default(),
-            Color::black(),
-            cx.font_cache(),
-        )
-        .unwrap();
-        let highlight_style = TextStyle::new(
-            "Menlo",
-            12.,
-            *FontProperties::new().weight(Weight::BOLD),
-            Default::default(),
-            Default::default(),
-            Color::new(255, 0, 0, 255),
-            cx.font_cache(),
-        )
-        .unwrap();
-        let label = Label::new(
-            ".αβγδε.ⓐⓑⓒⓓⓔ.abcde.".to_string(),
-            LabelStyle {
-                text: default_style.clone(),
-                highlight_text: Some(highlight_style.clone()),
-            },
-        )
-        .with_highlights(vec![
-            ".α".len(),
-            ".αβ".len(),
-            ".αβγδ".len(),
-            ".αβγδε.ⓐ".len(),
-            ".αβγδε.ⓐⓑ".len(),
-        ]);
-
-        let default_run_style = RunStyle {
-            font_id: default_style.font_id,
-            color: default_style.color,
-            underline: default_style.underline,
-        };
-        let highlight_run_style = RunStyle {
-            font_id: highlight_style.font_id,
-            color: highlight_style.color,
-            underline: highlight_style.underline,
-        };
-        let runs = label.compute_runs();
-        assert_eq!(
-            runs.as_slice(),
-            &[
-                (".α".len(), default_run_style),
-                ("βγ".len(), highlight_run_style),
-                ("δ".len(), default_run_style),
-                ("ε".len(), highlight_run_style),
-                (".ⓐ".len(), default_run_style),
-                ("ⓑⓒ".len(), highlight_run_style),
-                ("ⓓⓔ.abcde.".len(), default_run_style),
-            ]
-        );
-    }
-}

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

@@ -1,68 +1,54 @@
 use crate::{
-    geometry::{
-        rect::RectF,
-        vector::{vec2f, Vector2F},
-    },
-    json::json,
-    AnyElement, Element, MouseRegion, SizeConstraint, ViewContext,
+    point, px, AnyElement, AvailableSpace, BorrowAppContext, Bounds, DispatchPhase, Element,
+    IntoElement, Pixels, Point, ScrollWheelEvent, Size, Style, StyleRefinement, Styled,
+    WindowContext,
 };
-use std::{cell::RefCell, collections::VecDeque, fmt::Debug, ops::Range, rc::Rc};
+use collections::VecDeque;
+use refineable::Refineable as _;
+use std::{cell::RefCell, ops::Range, rc::Rc};
 use sum_tree::{Bias, SumTree};
 
-pub struct List<V> {
-    state: ListState<V>,
+pub fn list(state: ListState) -> List {
+    List {
+        state,
+        style: StyleRefinement::default(),
+    }
 }
 
-pub struct ListState<V>(Rc<RefCell<StateInner<V>>>);
-
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum Orientation {
-    Top,
-    Bottom,
+pub struct List {
+    state: ListState,
+    style: StyleRefinement,
 }
 
-struct StateInner<V> {
-    last_layout_width: Option<f32>,
-    render_item: Box<dyn FnMut(&mut V, usize, &mut ViewContext<V>) -> AnyElement<V>>,
-    rendered_range: Range<usize>,
-    items: SumTree<ListItem<V>>,
+#[derive(Clone)]
+pub struct ListState(Rc<RefCell<StateInner>>);
+
+struct StateInner {
+    last_layout_bounds: Option<Bounds<Pixels>>,
+    render_item: Box<dyn FnMut(usize, &mut WindowContext) -> AnyElement>,
+    items: SumTree<ListItem>,
     logical_scroll_top: Option<ListOffset>,
-    orientation: Orientation,
-    overdraw: f32,
+    alignment: ListAlignment,
+    overdraw: Pixels,
     #[allow(clippy::type_complexity)]
-    scroll_handler: Option<Box<dyn FnMut(Range<usize>, usize, &mut V, &mut ViewContext<V>)>>,
+    scroll_handler: Option<Box<dyn FnMut(&ListScrollEvent, &mut WindowContext)>>,
 }
 
-#[derive(Clone, Copy, Debug, Default, PartialEq)]
-pub struct ListOffset {
-    pub item_ix: usize,
-    pub offset_in_item: f32,
-}
-
-enum ListItem<V> {
-    Unrendered,
-    Rendered(Rc<RefCell<AnyElement<V>>>),
-    Removed(f32),
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum ListAlignment {
+    Top,
+    Bottom,
 }
 
-impl<V> Clone for ListItem<V> {
-    fn clone(&self) -> Self {
-        match self {
-            Self::Unrendered => Self::Unrendered,
-            Self::Rendered(element) => Self::Rendered(element.clone()),
-            Self::Removed(height) => Self::Removed(*height),
-        }
-    }
+pub struct ListScrollEvent {
+    pub visible_range: Range<usize>,
+    pub count: usize,
 }
 
-impl<V> Debug for ListItem<V> {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match self {
-            Self::Unrendered => write!(f, "Unrendered"),
-            Self::Rendered(_) => f.debug_tuple("Rendered").finish(),
-            Self::Removed(height) => f.debug_tuple("Removed").field(height).finish(),
-        }
-    }
+#[derive(Clone)]
+enum ListItem {
+    Unrendered,
+    Rendered { height: Pixels },
 }
 
 #[derive(Clone, Debug, Default, PartialEq)]
@@ -70,7 +56,7 @@ struct ListItemSummary {
     count: usize,
     rendered_count: usize,
     unrendered_count: usize,
-    height: f32,
+    height: Pixels,
 }
 
 #[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
@@ -83,286 +69,26 @@ struct RenderedCount(usize);
 struct UnrenderedCount(usize);
 
 #[derive(Clone, Debug, Default)]
-struct Height(f32);
-
-impl<V> List<V> {
-    pub fn new(state: ListState<V>) -> Self {
-        Self { state }
-    }
-}
-
-impl<V: 'static> Element<V> for List<V> {
-    type LayoutState = ListOffset;
-    type PaintState = ();
-
-    fn layout(
-        &mut self,
-        constraint: SizeConstraint,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> (Vector2F, Self::LayoutState) {
-        let state = &mut *self.state.0.borrow_mut();
-        let size = constraint.max;
-        let mut item_constraint = constraint;
-        item_constraint.min.set_y(0.);
-        item_constraint.max.set_y(f32::INFINITY);
-
-        if cx.refreshing() || state.last_layout_width != Some(size.x()) {
-            state.rendered_range = 0..0;
-            state.items = SumTree::from_iter(
-                (0..state.items.summary().count).map(|_| ListItem::Unrendered),
-                &(),
-            )
-        }
-
-        let old_items = state.items.clone();
-        let mut new_items = SumTree::new();
-        let mut rendered_items = VecDeque::new();
-        let mut rendered_height = 0.;
-        let mut scroll_top = state.logical_scroll_top();
+struct Height(Pixels);
 
-        // Render items after the scroll top, including those in the trailing overdraw.
-        let mut cursor = old_items.cursor::<Count>();
-        cursor.seek(&Count(scroll_top.item_ix), Bias::Right, &());
-        for (ix, item) in cursor.by_ref().enumerate() {
-            let visible_height = rendered_height - scroll_top.offset_in_item;
-            if visible_height >= size.y() + state.overdraw {
-                break;
-            }
-
-            // Force re-render if the item is visible, but attempt to re-use an existing one
-            // if we are inside the overdraw.
-            let existing_element = if visible_height >= size.y() {
-                Some(item)
-            } else {
-                None
-            };
-            if let Some(element) = state.render_item(
-                scroll_top.item_ix + ix,
-                existing_element,
-                item_constraint,
-                view,
-                cx,
-            ) {
-                rendered_height += element.borrow().size().y();
-                rendered_items.push_back(ListItem::Rendered(element));
-            }
-        }
-
-        // Prepare to start walking upward from the item at the scroll top.
-        cursor.seek(&Count(scroll_top.item_ix), Bias::Right, &());
-
-        // If the rendered items do not fill the visible region, then adjust
-        // the scroll top upward.
-        if rendered_height - scroll_top.offset_in_item < size.y() {
-            while rendered_height < size.y() {
-                cursor.prev(&());
-                if cursor.item().is_some() {
-                    if let Some(element) =
-                        state.render_item(cursor.start().0, None, item_constraint, view, cx)
-                    {
-                        rendered_height += element.borrow().size().y();
-                        rendered_items.push_front(ListItem::Rendered(element));
-                    }
-                } else {
-                    break;
-                }
-            }
-
-            scroll_top = ListOffset {
-                item_ix: cursor.start().0,
-                offset_in_item: rendered_height - size.y(),
-            };
-
-            match state.orientation {
-                Orientation::Top => {
-                    scroll_top.offset_in_item = scroll_top.offset_in_item.max(0.);
-                    state.logical_scroll_top = Some(scroll_top);
-                }
-                Orientation::Bottom => {
-                    scroll_top = ListOffset {
-                        item_ix: cursor.start().0,
-                        offset_in_item: rendered_height - size.y(),
-                    };
-                    state.logical_scroll_top = None;
-                }
-            };
-        }
-
-        // Render items in the leading overdraw.
-        let mut leading_overdraw = scroll_top.offset_in_item;
-        while leading_overdraw < state.overdraw {
-            cursor.prev(&());
-            if let Some(item) = cursor.item() {
-                if let Some(element) =
-                    state.render_item(cursor.start().0, Some(item), item_constraint, view, cx)
-                {
-                    leading_overdraw += element.borrow().size().y();
-                    rendered_items.push_front(ListItem::Rendered(element));
-                }
-            } else {
-                break;
-            }
-        }
-
-        let new_rendered_range = cursor.start().0..(cursor.start().0 + rendered_items.len());
-
-        let mut cursor = old_items.cursor::<Count>();
-
-        if state.rendered_range.start < new_rendered_range.start {
-            new_items.append(
-                cursor.slice(&Count(state.rendered_range.start), Bias::Right, &()),
-                &(),
-            );
-            let remove_to = state.rendered_range.end.min(new_rendered_range.start);
-            while cursor.start().0 < remove_to {
-                new_items.push(cursor.item().unwrap().remove(), &());
-                cursor.next(&());
-            }
-        }
-        new_items.append(
-            cursor.slice(&Count(new_rendered_range.start), Bias::Right, &()),
-            &(),
-        );
-
-        new_items.extend(rendered_items, &());
-        cursor.seek(&Count(new_rendered_range.end), Bias::Right, &());
-
-        if new_rendered_range.end < state.rendered_range.start {
-            new_items.append(
-                cursor.slice(&Count(state.rendered_range.start), Bias::Right, &()),
-                &(),
-            );
-        }
-        while cursor.start().0 < state.rendered_range.end {
-            new_items.push(cursor.item().unwrap().remove(), &());
-            cursor.next(&());
-        }
-
-        new_items.append(cursor.suffix(&()), &());
-
-        state.items = new_items;
-        state.rendered_range = new_rendered_range;
-        state.last_layout_width = Some(size.x());
-        (size, scroll_top)
-    }
-
-    fn paint(
-        &mut self,
-        bounds: RectF,
-        visible_bounds: RectF,
-        scroll_top: &mut ListOffset,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) {
-        let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();
-        cx.scene().push_layer(Some(visible_bounds));
-        let view_id = cx.view_id();
-        cx.scene()
-            .push_mouse_region(MouseRegion::new::<Self>(view_id, 0, bounds).on_scroll({
-                let state = self.state.clone();
-                let height = bounds.height();
-                let scroll_top = scroll_top.clone();
-                move |e, view, cx| {
-                    state.0.borrow_mut().scroll(
-                        &scroll_top,
-                        height,
-                        *e.platform_event.delta.raw(),
-                        e.platform_event.delta.precise(),
-                        view,
-                        cx,
-                    )
-                }
-            }));
-
-        let state = &mut *self.state.0.borrow_mut();
-        for (element, origin) in state.visible_elements(bounds, scroll_top) {
-            element.borrow_mut().paint(origin, visible_bounds, view, cx);
-        }
-
-        cx.scene().pop_layer();
-    }
-
-    fn rect_for_text_range(
-        &self,
-        range_utf16: Range<usize>,
-        bounds: RectF,
-        _: RectF,
-        scroll_top: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> Option<RectF> {
-        let state = self.state.0.borrow();
-        let mut item_origin = bounds.origin() - vec2f(0., scroll_top.offset_in_item);
-        let mut cursor = state.items.cursor::<Count>();
-        cursor.seek(&Count(scroll_top.item_ix), Bias::Right, &());
-        while let Some(item) = cursor.item() {
-            if item_origin.y() > bounds.max_y() {
-                break;
-            }
-
-            if let ListItem::Rendered(element) = item {
-                if let Some(rect) =
-                    element
-                        .borrow()
-                        .rect_for_text_range(range_utf16.clone(), view, cx)
-                {
-                    return Some(rect);
-                }
-
-                item_origin.set_y(item_origin.y() + element.borrow().size().y());
-                cursor.next(&());
-            } else {
-                unreachable!();
-            }
-        }
-
-        None
-    }
-
-    fn debug(
-        &self,
-        bounds: RectF,
-        scroll_top: &Self::LayoutState,
-        _: &(),
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> serde_json::Value {
-        let state = self.state.0.borrow_mut();
-        let visible_elements = state
-            .visible_elements(bounds, scroll_top)
-            .map(|e| e.0.borrow().debug(view, cx))
-            .collect::<Vec<_>>();
-        let visible_range = scroll_top.item_ix..(scroll_top.item_ix + visible_elements.len());
-        json!({
-            "visible_range": visible_range,
-            "visible_elements": visible_elements,
-            "scroll_top": state.logical_scroll_top.map(|top| (top.item_ix, top.offset_in_item)),
-        })
-    }
-}
-
-impl<V: 'static> ListState<V> {
-    pub fn new<D, F>(
+impl ListState {
+    pub fn new<F>(
         element_count: usize,
-        orientation: Orientation,
-        overdraw: f32,
-        mut render_item: F,
+        orientation: ListAlignment,
+        overdraw: Pixels,
+        render_item: F,
     ) -> Self
     where
-        D: Element<V>,
-        F: 'static + FnMut(&mut V, usize, &mut ViewContext<V>) -> D,
+        F: 'static + FnMut(usize, &mut WindowContext) -> AnyElement,
     {
         let mut items = SumTree::new();
         items.extend((0..element_count).map(|_| ListItem::Unrendered), &());
         Self(Rc::new(RefCell::new(StateInner {
-            last_layout_width: None,
-            render_item: Box::new(move |view, ix, cx| render_item(view, ix, cx).into_any()),
-            rendered_range: 0..0,
+            last_layout_bounds: None,
+            render_item: Box::new(render_item),
             items,
             logical_scroll_top: None,
-            orientation,
+            alignment: orientation,
             overdraw,
             scroll_handler: None,
         })))
@@ -370,7 +96,6 @@ impl<V: 'static> ListState<V> {
 
     pub fn reset(&self, element_count: usize) {
         let state = &mut *self.0.borrow_mut();
-        state.rendered_range = 0..0;
         state.logical_scroll_top = None;
         state.items = SumTree::new();
         state
@@ -392,22 +117,12 @@ impl<V: 'static> ListState<V> {
         {
             if old_range.contains(item_ix) {
                 *item_ix = old_range.start;
-                *offset_in_item = 0.;
+                *offset_in_item = px(0.);
             } else if old_range.end <= *item_ix {
                 *item_ix = *item_ix - (old_range.end - old_range.start) + count;
             }
         }
 
-        let new_end = old_range.start + count;
-        if old_range.start < state.rendered_range.start {
-            state.rendered_range.start =
-                new_end + state.rendered_range.start.saturating_sub(old_range.end);
-        }
-        if old_range.start < state.rendered_range.end {
-            state.rendered_range.end =
-                new_end + state.rendered_range.end.saturating_sub(old_range.end);
-        }
-
         let mut old_heights = state.items.cursor::<Count>();
         let mut new_heights = old_heights.slice(&Count(old_range.start), Bias::Right, &());
         old_heights.seek_forward(&Count(old_range.end), Bias::Right, &());
@@ -419,8 +134,8 @@ impl<V: 'static> ListState<V> {
     }
 
     pub fn set_scroll_handler(
-        &mut self,
-        handler: impl FnMut(Range<usize>, usize, &mut V, &mut ViewContext<V>) + 'static,
+        &self,
+        handler: impl FnMut(&ListScrollEvent, &mut WindowContext) + 'static,
     ) {
         self.0.borrow_mut().scroll_handler = Some(Box::new(handler))
     }
@@ -434,91 +149,92 @@ impl<V: 'static> ListState<V> {
         let item_count = state.items.summary().count;
         if scroll_top.item_ix >= item_count {
             scroll_top.item_ix = item_count;
-            scroll_top.offset_in_item = 0.;
+            scroll_top.offset_in_item = px(0.);
         }
         state.logical_scroll_top = Some(scroll_top);
     }
-}
 
-impl<V> Clone for ListState<V> {
-    fn clone(&self) -> Self {
-        Self(self.0.clone())
-    }
-}
+    pub fn scroll_to_reveal_item(&self, ix: usize) {
+        let state = &mut *self.0.borrow_mut();
+        let mut scroll_top = state.logical_scroll_top();
+        let height = state
+            .last_layout_bounds
+            .map_or(px(0.), |bounds| bounds.size.height);
 
-impl<V: 'static> StateInner<V> {
-    fn render_item(
-        &mut self,
-        ix: usize,
-        existing_element: Option<&ListItem<V>>,
-        constraint: SizeConstraint,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> Option<Rc<RefCell<AnyElement<V>>>> {
-        if let Some(ListItem::Rendered(element)) = existing_element {
-            Some(element.clone())
+        if ix <= scroll_top.item_ix {
+            scroll_top.item_ix = ix;
+            scroll_top.offset_in_item = px(0.);
         } else {
-            let mut element = (self.render_item)(view, ix, cx);
-            element.layout(constraint, view, cx);
-            Some(Rc::new(RefCell::new(element)))
+            let mut cursor = state.items.cursor::<ListItemSummary>();
+            cursor.seek(&Count(ix + 1), Bias::Right, &());
+            let bottom = cursor.start().height;
+            let goal_top = px(0.).max(bottom - height);
+
+            cursor.seek(&Height(goal_top), Bias::Left, &());
+            let start_ix = cursor.start().count;
+            let start_item_top = cursor.start().height;
+
+            if start_ix >= scroll_top.item_ix {
+                scroll_top.item_ix = start_ix;
+                scroll_top.offset_in_item = goal_top - start_item_top;
+            }
         }
-    }
 
-    fn visible_range(&self, height: f32, scroll_top: &ListOffset) -> Range<usize> {
-        let mut cursor = self.items.cursor::<ListItemSummary>();
-        cursor.seek(&Count(scroll_top.item_ix), Bias::Right, &());
-        let start_y = cursor.start().height + scroll_top.offset_in_item;
-        cursor.seek_forward(&Height(start_y + height), Bias::Left, &());
-        scroll_top.item_ix..cursor.start().count + 1
+        state.logical_scroll_top = Some(scroll_top);
     }
 
-    fn visible_elements<'a>(
-        &'a self,
-        bounds: RectF,
-        scroll_top: &ListOffset,
-    ) -> impl Iterator<Item = (Rc<RefCell<AnyElement<V>>>, Vector2F)> + 'a {
-        let mut item_origin = bounds.origin() - vec2f(0., scroll_top.offset_in_item);
-        let mut cursor = self.items.cursor::<Count>();
-        cursor.seek(&Count(scroll_top.item_ix), Bias::Right, &());
-        std::iter::from_fn(move || {
-            while let Some(item) = cursor.item() {
-                if item_origin.y() > bounds.max_y() {
-                    break;
-                }
+    /// Get the bounds for the given item in window coordinates.
+    pub fn bounds_for_item(&self, ix: usize) -> Option<Bounds<Pixels>> {
+        let state = &*self.0.borrow();
+        let bounds = state.last_layout_bounds.unwrap_or_default();
+        let scroll_top = state.logical_scroll_top();
 
-                if let ListItem::Rendered(element) = item {
-                    let result = (element.clone(), item_origin);
-                    item_origin.set_y(item_origin.y() + element.borrow().size().y());
-                    cursor.next(&());
-                    return Some(result);
-                }
+        if ix < scroll_top.item_ix {
+            return None;
+        }
+
+        let mut cursor = state.items.cursor::<(Count, Height)>();
+        cursor.seek(&Count(scroll_top.item_ix), Bias::Right, &());
 
-                cursor.next(&());
+        let scroll_top = cursor.start().1 .0 + scroll_top.offset_in_item;
+
+        cursor.seek_forward(&Count(ix), Bias::Right, &());
+        if let Some(&ListItem::Rendered { height }) = cursor.item() {
+            let &(Count(count), Height(top)) = cursor.start();
+            if count == ix {
+                let top = bounds.top() + top - scroll_top;
+                return Some(Bounds::from_corners(
+                    point(bounds.left(), top),
+                    point(bounds.right(), top + height),
+                ));
             }
+        }
+        None
+    }
+}
 
-            None
-        })
+impl StateInner {
+    fn visible_range(&self, height: Pixels, scroll_top: &ListOffset) -> Range<usize> {
+        let mut cursor = self.items.cursor::<ListItemSummary>();
+        cursor.seek(&Count(scroll_top.item_ix), Bias::Right, &());
+        let start_y = cursor.start().height + scroll_top.offset_in_item;
+        cursor.seek_forward(&Height(start_y + height), Bias::Left, &());
+        scroll_top.item_ix..cursor.start().count + 1
     }
 
     fn scroll(
         &mut self,
         scroll_top: &ListOffset,
-        height: f32,
-        mut delta: Vector2F,
-        precise: bool,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
+        height: Pixels,
+        delta: Point<Pixels>,
+        cx: &mut WindowContext,
     ) {
-        if !precise {
-            delta *= 20.;
-        }
-
-        let scroll_max = (self.items.summary().height - height).max(0.);
-        let new_scroll_top = (self.scroll_top(scroll_top) - delta.y())
-            .max(0.)
+        let scroll_max = (self.items.summary().height - height).max(px(0.));
+        let new_scroll_top = (self.scroll_top(scroll_top) - delta.y)
+            .max(px(0.))
             .min(scroll_max);
 
-        if self.orientation == Orientation::Bottom && new_scroll_top == scroll_max {
+        if self.alignment == ListAlignment::Bottom && new_scroll_top == scroll_max {
             self.logical_scroll_top = None;
         } else {
             let mut cursor = self.items.cursor::<ListItemSummary>();
@@ -534,9 +250,10 @@ impl<V: 'static> StateInner<V> {
         if self.scroll_handler.is_some() {
             let visible_range = self.visible_range(height, scroll_top);
             self.scroll_handler.as_mut().unwrap()(
-                visible_range,
-                self.items.summary().count,
-                view,
+                &ListScrollEvent {
+                    visible_range,
+                    count: self.items.summary().count,
+                },
                 cx,
             );
         }
@@ -546,36 +263,235 @@ impl<V: 'static> StateInner<V> {
 
     fn logical_scroll_top(&self) -> ListOffset {
         self.logical_scroll_top
-            .unwrap_or_else(|| match self.orientation {
-                Orientation::Top => ListOffset {
+            .unwrap_or_else(|| match self.alignment {
+                ListAlignment::Top => ListOffset {
                     item_ix: 0,
-                    offset_in_item: 0.,
+                    offset_in_item: px(0.),
                 },
-                Orientation::Bottom => ListOffset {
+                ListAlignment::Bottom => ListOffset {
                     item_ix: self.items.summary().count,
-                    offset_in_item: 0.,
+                    offset_in_item: px(0.),
                 },
             })
     }
 
-    fn scroll_top(&self, logical_scroll_top: &ListOffset) -> f32 {
+    fn scroll_top(&self, logical_scroll_top: &ListOffset) -> Pixels {
         let mut cursor = self.items.cursor::<ListItemSummary>();
         cursor.seek(&Count(logical_scroll_top.item_ix), Bias::Right, &());
         cursor.start().height + logical_scroll_top.offset_in_item
     }
 }
 
-impl<V> ListItem<V> {
-    fn remove(&self) -> Self {
+impl std::fmt::Debug for ListItem {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         match self {
-            ListItem::Unrendered => ListItem::Unrendered,
-            ListItem::Rendered(element) => ListItem::Removed(element.borrow().size().y()),
-            ListItem::Removed(height) => ListItem::Removed(*height),
+            Self::Unrendered => write!(f, "Unrendered"),
+            Self::Rendered { height, .. } => {
+                f.debug_struct("Rendered").field("height", height).finish()
+            }
+        }
+    }
+}
+
+#[derive(Debug, Clone, Copy, Default)]
+pub struct ListOffset {
+    pub item_ix: usize,
+    pub offset_in_item: Pixels,
+}
+
+impl Element for List {
+    type State = ();
+
+    fn request_layout(
+        &mut self,
+        _state: Option<Self::State>,
+        cx: &mut crate::WindowContext,
+    ) -> (crate::LayoutId, Self::State) {
+        let mut style = Style::default();
+        style.refine(&self.style);
+        let layout_id = cx.with_text_style(style.text_style().cloned(), |cx| {
+            cx.request_layout(&style, None)
+        });
+        (layout_id, ())
+    }
+
+    fn paint(
+        &mut self,
+        bounds: crate::Bounds<crate::Pixels>,
+        _state: &mut Self::State,
+        cx: &mut crate::WindowContext,
+    ) {
+        let state = &mut *self.state.0.borrow_mut();
+
+        // If the width of the list has changed, invalidate all cached item heights
+        if state.last_layout_bounds.map_or(true, |last_bounds| {
+            last_bounds.size.width != bounds.size.width
+        }) {
+            state.items = SumTree::from_iter(
+                (0..state.items.summary().count).map(|_| ListItem::Unrendered),
+                &(),
+            )
+        }
+
+        let old_items = state.items.clone();
+        let mut measured_items = VecDeque::new();
+        let mut item_elements = VecDeque::new();
+        let mut rendered_height = px(0.);
+        let mut scroll_top = state.logical_scroll_top();
+
+        let available_item_space = Size {
+            width: AvailableSpace::Definite(bounds.size.width),
+            height: AvailableSpace::MinContent,
+        };
+
+        // Render items after the scroll top, including those in the trailing overdraw
+        let mut cursor = old_items.cursor::<Count>();
+        cursor.seek(&Count(scroll_top.item_ix), Bias::Right, &());
+        for (ix, item) in cursor.by_ref().enumerate() {
+            let visible_height = rendered_height - scroll_top.offset_in_item;
+            if visible_height >= bounds.size.height + state.overdraw {
+                break;
+            }
+
+            // Use the previously cached height if available
+            let mut height = if let ListItem::Rendered { height } = item {
+                Some(*height)
+            } else {
+                None
+            };
+
+            // If we're within the visible area or the height wasn't cached, render and measure the item's element
+            if visible_height < bounds.size.height || height.is_none() {
+                let mut element = (state.render_item)(scroll_top.item_ix + ix, cx);
+                let element_size = element.measure(available_item_space, cx);
+                height = Some(element_size.height);
+                if visible_height < bounds.size.height {
+                    item_elements.push_back(element);
+                }
+            }
+
+            let height = height.unwrap();
+            rendered_height += height;
+            measured_items.push_back(ListItem::Rendered { height });
+        }
+
+        // Prepare to start walking upward from the item at the scroll top.
+        cursor.seek(&Count(scroll_top.item_ix), Bias::Right, &());
+
+        // If the rendered items do not fill the visible region, then adjust
+        // the scroll top upward.
+        if rendered_height - scroll_top.offset_in_item < bounds.size.height {
+            while rendered_height < bounds.size.height {
+                cursor.prev(&());
+                if cursor.item().is_some() {
+                    let mut element = (state.render_item)(cursor.start().0, cx);
+                    let element_size = element.measure(available_item_space, cx);
+
+                    rendered_height += element_size.height;
+                    measured_items.push_front(ListItem::Rendered {
+                        height: element_size.height,
+                    });
+                    item_elements.push_front(element)
+                } else {
+                    break;
+                }
+            }
+
+            scroll_top = ListOffset {
+                item_ix: cursor.start().0,
+                offset_in_item: rendered_height - bounds.size.height,
+            };
+
+            match state.alignment {
+                ListAlignment::Top => {
+                    scroll_top.offset_in_item = scroll_top.offset_in_item.max(px(0.));
+                    state.logical_scroll_top = Some(scroll_top);
+                }
+                ListAlignment::Bottom => {
+                    scroll_top = ListOffset {
+                        item_ix: cursor.start().0,
+                        offset_in_item: rendered_height - bounds.size.height,
+                    };
+                    state.logical_scroll_top = None;
+                }
+            };
+        }
+
+        // Measure items in the leading overdraw
+        let mut leading_overdraw = scroll_top.offset_in_item;
+        while leading_overdraw < state.overdraw {
+            cursor.prev(&());
+            if let Some(item) = cursor.item() {
+                let height = if let ListItem::Rendered { height } = item {
+                    *height
+                } else {
+                    let mut element = (state.render_item)(cursor.start().0, cx);
+                    element.measure(available_item_space, cx).height
+                };
+
+                leading_overdraw += height;
+                measured_items.push_front(ListItem::Rendered { height });
+            } else {
+                break;
+            }
+        }
+
+        let measured_range = cursor.start().0..(cursor.start().0 + measured_items.len());
+        let mut cursor = old_items.cursor::<Count>();
+        let mut new_items = cursor.slice(&Count(measured_range.start), Bias::Right, &());
+        new_items.extend(measured_items, &());
+        cursor.seek(&Count(measured_range.end), Bias::Right, &());
+        new_items.append(cursor.suffix(&()), &());
+
+        // Paint the visible items
+        let mut item_origin = bounds.origin;
+        item_origin.y -= scroll_top.offset_in_item;
+        for item_element in &mut item_elements {
+            let item_height = item_element.measure(available_item_space, cx).height;
+            item_element.draw(item_origin, available_item_space, cx);
+            item_origin.y += item_height;
         }
+
+        state.items = new_items;
+        state.last_layout_bounds = Some(bounds);
+
+        let list_state = self.state.clone();
+        let height = bounds.size.height;
+        cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
+            if phase == DispatchPhase::Bubble
+                && bounds.contains(&event.position)
+                && cx.was_top_layer(&event.position, cx.stacking_order())
+            {
+                list_state.0.borrow_mut().scroll(
+                    &scroll_top,
+                    height,
+                    event.delta.pixel_delta(px(20.)),
+                    cx,
+                )
+            }
+        });
+    }
+}
+
+impl IntoElement for List {
+    type Element = Self;
+
+    fn element_id(&self) -> Option<crate::ElementId> {
+        None
+    }
+
+    fn into_element(self) -> Self::Element {
+        self
     }
 }
 
-impl<V> sum_tree::Item for ListItem<V> {
+impl Styled for List {
+    fn style(&mut self) -> &mut StyleRefinement {
+        &mut self.style
+    }
+}
+
+impl sum_tree::Item for ListItem {
     type Summary = ListItemSummary;
 
     fn summary(&self) -> Self::Summary {
@@ -584,18 +500,12 @@ impl<V> sum_tree::Item for ListItem<V> {
                 count: 1,
                 rendered_count: 0,
                 unrendered_count: 1,
-                height: 0.,
+                height: px(0.),
             },
-            ListItem::Rendered(element) => ListItemSummary {
+            ListItem::Rendered { height } => ListItemSummary {
                 count: 1,
                 rendered_count: 1,
                 unrendered_count: 0,
-                height: element.borrow().size().y(),
-            },
-            ListItem::Removed(height) => ListItemSummary {
-                count: 1,
-                rendered_count: 0,
-                unrendered_count: 1,
                 height: *height,
             },
         }
@@ -648,339 +558,3 @@ impl<'a> sum_tree::SeekTarget<'a, ListItemSummary, ListItemSummary> for Height {
         self.0.partial_cmp(&other.height).unwrap()
     }
 }
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::{elements::Empty, geometry::vector::vec2f, Entity};
-    use rand::prelude::*;
-    use std::env;
-
-    #[crate::test(self)]
-    fn test_layout(cx: &mut crate::AppContext) {
-        cx.add_window(Default::default(), |cx| {
-            let mut view = TestView;
-            let constraint = SizeConstraint::new(vec2f(0., 0.), vec2f(100., 40.));
-            let elements = Rc::new(RefCell::new(vec![(0, 20.), (1, 30.), (2, 100.)]));
-            let state = ListState::new(elements.borrow().len(), Orientation::Top, 1000.0, {
-                let elements = elements.clone();
-                move |_, ix, _| {
-                    let (id, height) = elements.borrow()[ix];
-                    TestElement::new(id, height).into_any()
-                }
-            });
-
-            let mut list = List::new(state.clone());
-            let (size, _) = list.layout(constraint, &mut view, cx);
-            assert_eq!(size, vec2f(100., 40.));
-            assert_eq!(
-                state.0.borrow().items.summary().clone(),
-                ListItemSummary {
-                    count: 3,
-                    rendered_count: 3,
-                    unrendered_count: 0,
-                    height: 150.
-                }
-            );
-
-            state.0.borrow_mut().scroll(
-                &ListOffset {
-                    item_ix: 0,
-                    offset_in_item: 0.,
-                },
-                40.,
-                vec2f(0., -54.),
-                true,
-                &mut view,
-                cx,
-            );
-
-            let (_, logical_scroll_top) = list.layout(constraint, &mut view, cx);
-            assert_eq!(
-                logical_scroll_top,
-                ListOffset {
-                    item_ix: 2,
-                    offset_in_item: 4.
-                }
-            );
-            assert_eq!(state.0.borrow().scroll_top(&logical_scroll_top), 54.);
-
-            elements.borrow_mut().splice(1..2, vec![(3, 40.), (4, 50.)]);
-            elements.borrow_mut().push((5, 60.));
-            state.splice(1..2, 2);
-            state.splice(4..4, 1);
-            assert_eq!(
-                state.0.borrow().items.summary().clone(),
-                ListItemSummary {
-                    count: 5,
-                    rendered_count: 2,
-                    unrendered_count: 3,
-                    height: 120.
-                }
-            );
-
-            let (size, logical_scroll_top) = list.layout(constraint, &mut view, cx);
-            assert_eq!(size, vec2f(100., 40.));
-            assert_eq!(
-                state.0.borrow().items.summary().clone(),
-                ListItemSummary {
-                    count: 5,
-                    rendered_count: 5,
-                    unrendered_count: 0,
-                    height: 270.
-                }
-            );
-            assert_eq!(
-                logical_scroll_top,
-                ListOffset {
-                    item_ix: 3,
-                    offset_in_item: 4.
-                }
-            );
-            assert_eq!(state.0.borrow().scroll_top(&logical_scroll_top), 114.);
-
-            view
-        });
-    }
-
-    #[crate::test(self, iterations = 10)]
-    fn test_random(cx: &mut crate::AppContext, mut rng: StdRng) {
-        let operations = env::var("OPERATIONS")
-            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
-            .unwrap_or(10);
-
-        cx.add_window(Default::default(), |cx| {
-            let mut view = TestView;
-
-            let mut next_id = 0;
-            let elements = Rc::new(RefCell::new(
-                (0..rng.gen_range(0..=20))
-                    .map(|_| {
-                        let id = next_id;
-                        next_id += 1;
-                        (id, rng.gen_range(0..=200) as f32 / 2.0)
-                    })
-                    .collect::<Vec<_>>(),
-            ));
-            let orientation = *[Orientation::Top, Orientation::Bottom]
-                .choose(&mut rng)
-                .unwrap();
-            let overdraw = rng.gen_range(1..=100) as f32;
-
-            let state = ListState::new(elements.borrow().len(), orientation, overdraw, {
-                let elements = elements.clone();
-                move |_, ix, _| {
-                    let (id, height) = elements.borrow()[ix];
-                    TestElement::new(id, height).into_any()
-                }
-            });
-
-            let mut width = rng.gen_range(0..=2000) as f32 / 2.;
-            let mut height = rng.gen_range(0..=2000) as f32 / 2.;
-            log::info!("orientation: {:?}", orientation);
-            log::info!("overdraw: {}", overdraw);
-            log::info!("elements: {:?}", elements.borrow());
-            log::info!("size: ({:?}, {:?})", width, height);
-            log::info!("==================");
-
-            let mut last_logical_scroll_top = None;
-            for _ in 0..operations {
-                match rng.gen_range(0..=100) {
-                    0..=29 if last_logical_scroll_top.is_some() => {
-                        let delta = vec2f(0., rng.gen_range(-overdraw..=overdraw));
-                        log::info!(
-                            "Scrolling by {:?}, previous scroll top: {:?}",
-                            delta,
-                            last_logical_scroll_top.unwrap()
-                        );
-                        state.0.borrow_mut().scroll(
-                            last_logical_scroll_top.as_ref().unwrap(),
-                            height,
-                            delta,
-                            true,
-                            &mut view,
-                            cx,
-                        );
-                    }
-                    30..=34 => {
-                        width = rng.gen_range(0..=2000) as f32 / 2.;
-                        log::info!("changing width: {:?}", width);
-                    }
-                    35..=54 => {
-                        height = rng.gen_range(0..=1000) as f32 / 2.;
-                        log::info!("changing height: {:?}", height);
-                    }
-                    _ => {
-                        let mut elements = elements.borrow_mut();
-                        let end_ix = rng.gen_range(0..=elements.len());
-                        let start_ix = rng.gen_range(0..=end_ix);
-                        let new_elements = (0..rng.gen_range(0..10))
-                            .map(|_| {
-                                let id = next_id;
-                                next_id += 1;
-                                (id, rng.gen_range(0..=200) as f32 / 2.)
-                            })
-                            .collect::<Vec<_>>();
-                        log::info!("splice({:?}, {:?})", start_ix..end_ix, new_elements);
-                        state.splice(start_ix..end_ix, new_elements.len());
-                        elements.splice(start_ix..end_ix, new_elements);
-                        for (ix, item) in state.0.borrow().items.cursor::<()>().enumerate() {
-                            if let ListItem::Rendered(element) = item {
-                                let (expected_id, _) = elements[ix];
-                                element.borrow().with_metadata(|metadata: Option<&usize>| {
-                                    assert_eq!(*metadata.unwrap(), expected_id);
-                                });
-                            }
-                        }
-                    }
-                }
-
-                let mut list = List::new(state.clone());
-                let window_size = vec2f(width, height);
-                let (size, logical_scroll_top) = list.layout(
-                    SizeConstraint::new(vec2f(0., 0.), window_size),
-                    &mut view,
-                    cx,
-                );
-                assert_eq!(size, window_size);
-                last_logical_scroll_top = Some(logical_scroll_top);
-
-                let state = state.0.borrow();
-                log::info!("items {:?}", state.items.items(&()));
-
-                let scroll_top = state.scroll_top(&logical_scroll_top);
-                let rendered_top = (scroll_top - overdraw).max(0.);
-                let rendered_bottom = scroll_top + height + overdraw;
-                let mut item_top = 0.;
-
-                log::info!(
-                    "rendered top {:?}, rendered bottom {:?}, scroll top {:?}",
-                    rendered_top,
-                    rendered_bottom,
-                    scroll_top,
-                );
-
-                let mut first_rendered_element_top = None;
-                let mut last_rendered_element_bottom = None;
-                assert_eq!(state.items.summary().count, elements.borrow().len());
-                for (ix, item) in state.items.cursor::<()>().enumerate() {
-                    match item {
-                        ListItem::Unrendered => {
-                            let item_bottom = item_top;
-                            assert!(item_bottom <= rendered_top || item_top >= rendered_bottom);
-                            item_top = item_bottom;
-                        }
-                        ListItem::Removed(height) => {
-                            let (id, expected_height) = elements.borrow()[ix];
-                            assert_eq!(
-                                *height, expected_height,
-                                "element {} height didn't match",
-                                id
-                            );
-                            let item_bottom = item_top + height;
-                            assert!(item_bottom <= rendered_top || item_top >= rendered_bottom);
-                            item_top = item_bottom;
-                        }
-                        ListItem::Rendered(element) => {
-                            let (expected_id, expected_height) = elements.borrow()[ix];
-                            let element = element.borrow();
-                            element.with_metadata(|metadata: Option<&usize>| {
-                                assert_eq!(*metadata.unwrap(), expected_id);
-                            });
-                            assert_eq!(element.size().y(), expected_height);
-                            let item_bottom = item_top + element.size().y();
-                            first_rendered_element_top.get_or_insert(item_top);
-                            last_rendered_element_bottom = Some(item_bottom);
-                            assert!(item_bottom > rendered_top || item_top < rendered_bottom);
-                            item_top = item_bottom;
-                        }
-                    }
-                }
-
-                match orientation {
-                    Orientation::Top => {
-                        if let Some(first_rendered_element_top) = first_rendered_element_top {
-                            assert!(first_rendered_element_top <= scroll_top);
-                        }
-                    }
-                    Orientation::Bottom => {
-                        if let Some(last_rendered_element_bottom) = last_rendered_element_bottom {
-                            assert!(last_rendered_element_bottom >= scroll_top + height);
-                        }
-                    }
-                }
-            }
-
-            view
-        });
-    }
-
-    struct TestView;
-
-    impl Entity for TestView {
-        type Event = ();
-    }
-
-    impl crate::View for TestView {
-        fn ui_name() -> &'static str {
-            "TestView"
-        }
-
-        fn render(&mut self, _: &mut ViewContext<Self>) -> AnyElement<Self> {
-            Empty::new().into_any()
-        }
-    }
-
-    struct TestElement {
-        id: usize,
-        size: Vector2F,
-    }
-
-    impl TestElement {
-        fn new(id: usize, height: f32) -> Self {
-            Self {
-                id,
-                size: vec2f(100., height),
-            }
-        }
-    }
-
-    impl<V: 'static> Element<V> for TestElement {
-        type LayoutState = ();
-        type PaintState = ();
-
-        fn layout(
-            &mut self,
-            _: SizeConstraint,
-            _: &mut V,
-            _: &mut ViewContext<V>,
-        ) -> (Vector2F, ()) {
-            (self.size, ())
-        }
-
-        fn paint(&mut self, _: RectF, _: RectF, _: &mut (), _: &mut V, _: &mut ViewContext<V>) {
-            unimplemented!()
-        }
-
-        fn rect_for_text_range(
-            &self,
-            _: Range<usize>,
-            _: RectF,
-            _: RectF,
-            _: &Self::LayoutState,
-            _: &Self::PaintState,
-            _: &V,
-            _: &ViewContext<V>,
-        ) -> Option<RectF> {
-            unimplemented!()
-        }
-
-        fn debug(&self, _: RectF, _: &(), _: &(), _: &V, _: &ViewContext<V>) -> serde_json::Value {
-            self.id.into()
-        }
-
-        fn metadata(&self) -> Option<&dyn std::any::Any> {
-            Some(&self.id)
-        }
-    }
-}

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

@@ -1,323 +0,0 @@
-use super::Padding;
-use crate::{
-    geometry::{
-        rect::RectF,
-        vector::{vec2f, Vector2F},
-    },
-    platform::CursorStyle,
-    platform::MouseButton,
-    scene::{
-        CursorRegion, HandlerSet, MouseClick, MouseClickOut, MouseDown, MouseDownOut, MouseDrag,
-        MouseHover, MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut,
-    },
-    AnyElement, Element, EventContext, MouseRegion, MouseState, SizeConstraint, TypeTag,
-    ViewContext,
-};
-use serde_json::json;
-use std::ops::Range;
-
-pub struct MouseEventHandler<V: 'static> {
-    child: AnyElement<V>,
-    region_id: usize,
-    cursor_style: Option<CursorStyle>,
-    handlers: HandlerSet,
-    hoverable: bool,
-    notify_on_hover: bool,
-    notify_on_click: bool,
-    above: bool,
-    padding: Padding,
-    tag: TypeTag,
-}
-
-/// Element which provides a render_child callback with a MouseState and paints a mouse
-/// region under (or above) it for easy mouse event handling.
-impl<V: 'static> MouseEventHandler<V> {
-    pub fn for_child<Tag: 'static>(child: impl Element<V>, region_id: usize) -> Self {
-        Self {
-            child: child.into_any(),
-            region_id,
-            cursor_style: None,
-            handlers: Default::default(),
-            notify_on_hover: false,
-            notify_on_click: false,
-            hoverable: false,
-            above: false,
-            padding: Default::default(),
-            tag: TypeTag::new::<Tag>(),
-        }
-    }
-
-    pub fn new<Tag: 'static, E>(
-        region_id: usize,
-        cx: &mut ViewContext<V>,
-        render_child: impl FnOnce(&mut MouseState, &mut ViewContext<V>) -> E,
-    ) -> Self
-    where
-        E: Element<V>,
-    {
-        let mut mouse_state = cx.mouse_state_dynamic(TypeTag::new::<Tag>(), region_id);
-        let child = render_child(&mut mouse_state, cx).into_any();
-        let notify_on_hover = mouse_state.accessed_hovered();
-        let notify_on_click = mouse_state.accessed_clicked();
-        Self {
-            child,
-            region_id,
-            cursor_style: None,
-            handlers: Default::default(),
-            notify_on_hover,
-            notify_on_click,
-            hoverable: true,
-            above: false,
-            padding: Default::default(),
-            tag: TypeTag::new::<Tag>(),
-        }
-    }
-
-    pub fn new_dynamic(
-        tag: TypeTag,
-        region_id: usize,
-        cx: &mut ViewContext<V>,
-        render_child: impl FnOnce(&mut MouseState, &mut ViewContext<V>) -> AnyElement<V>,
-    ) -> Self {
-        let mut mouse_state = cx.mouse_state_dynamic(tag, region_id);
-        let child = render_child(&mut mouse_state, cx);
-        let notify_on_hover = mouse_state.accessed_hovered();
-        let notify_on_click = mouse_state.accessed_clicked();
-        Self {
-            child,
-            region_id,
-            cursor_style: None,
-            handlers: Default::default(),
-            notify_on_hover,
-            notify_on_click,
-            hoverable: true,
-            above: false,
-            padding: Default::default(),
-            tag,
-        }
-    }
-
-    /// Modifies the MouseEventHandler to render the MouseRegion above the child element. Useful
-    /// for drag and drop handling and similar events which should be captured before the child
-    /// gets the opportunity
-    pub fn above<Tag: 'static, D>(
-        region_id: usize,
-        cx: &mut ViewContext<V>,
-        render_child: impl FnOnce(&mut MouseState, &mut ViewContext<V>) -> D,
-    ) -> Self
-    where
-        D: Element<V>,
-    {
-        let mut handler = Self::new::<Tag, _>(region_id, cx, render_child);
-        handler.above = true;
-        handler
-    }
-
-    pub fn with_cursor_style(mut self, cursor: CursorStyle) -> Self {
-        self.cursor_style = Some(cursor);
-        self
-    }
-
-    pub fn capture_all(mut self) -> Self {
-        self.handlers = HandlerSet::capture_all();
-        self
-    }
-
-    pub fn on_move(
-        mut self,
-        handler: impl Fn(MouseMove, &mut V, &mut EventContext<V>) + 'static,
-    ) -> Self {
-        self.handlers = self.handlers.on_move(handler);
-        self
-    }
-
-    pub fn on_move_out(
-        mut self,
-        handler: impl Fn(MouseMoveOut, &mut V, &mut EventContext<V>) + 'static,
-    ) -> Self {
-        self.handlers = self.handlers.on_move_out(handler);
-        self
-    }
-
-    pub fn on_down(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(MouseDown, &mut V, &mut EventContext<V>) + 'static,
-    ) -> Self {
-        self.handlers = self.handlers.on_down(button, handler);
-        self
-    }
-
-    pub fn on_up(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(MouseUp, &mut V, &mut EventContext<V>) + 'static,
-    ) -> Self {
-        self.handlers = self.handlers.on_up(button, handler);
-        self
-    }
-
-    pub fn on_click(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
-    ) -> Self {
-        self.handlers = self.handlers.on_click(button, handler);
-        self
-    }
-
-    pub fn on_click_out(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(MouseClickOut, &mut V, &mut EventContext<V>) + 'static,
-    ) -> Self {
-        self.handlers = self.handlers.on_click_out(button, handler);
-        self
-    }
-
-    pub fn on_down_out(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(MouseDownOut, &mut V, &mut EventContext<V>) + 'static,
-    ) -> Self {
-        self.handlers = self.handlers.on_down_out(button, handler);
-        self
-    }
-
-    pub fn on_up_out(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(MouseUpOut, &mut V, &mut EventContext<V>) + 'static,
-    ) -> Self {
-        self.handlers = self.handlers.on_up_out(button, handler);
-        self
-    }
-
-    pub fn on_drag(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(MouseDrag, &mut V, &mut EventContext<V>) + 'static,
-    ) -> Self {
-        self.handlers = self.handlers.on_drag(button, handler);
-        self
-    }
-
-    pub fn on_hover(
-        mut self,
-        handler: impl Fn(MouseHover, &mut V, &mut EventContext<V>) + 'static,
-    ) -> Self {
-        self.handlers = self.handlers.on_hover(handler);
-        self
-    }
-
-    pub fn on_scroll(
-        mut self,
-        handler: impl Fn(MouseScrollWheel, &mut V, &mut EventContext<V>) + 'static,
-    ) -> Self {
-        self.handlers = self.handlers.on_scroll(handler);
-        self
-    }
-
-    pub fn with_hoverable(mut self, is_hoverable: bool) -> Self {
-        self.hoverable = is_hoverable;
-        self
-    }
-
-    pub fn with_padding(mut self, padding: Padding) -> Self {
-        self.padding = padding;
-        self
-    }
-
-    fn hit_bounds(&self, bounds: RectF) -> RectF {
-        RectF::from_points(
-            bounds.origin() - vec2f(self.padding.left, self.padding.top),
-            bounds.lower_right() + vec2f(self.padding.right, self.padding.bottom),
-        )
-        .round_out()
-    }
-
-    fn paint_regions(&self, bounds: RectF, visible_bounds: RectF, cx: &mut ViewContext<V>) {
-        let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();
-        let hit_bounds = self.hit_bounds(visible_bounds);
-
-        if let Some(style) = self.cursor_style {
-            cx.scene().push_cursor_region(CursorRegion {
-                bounds: hit_bounds,
-                style,
-            });
-        }
-        let view_id = cx.view_id();
-        cx.scene().push_mouse_region(
-            MouseRegion::from_handlers(
-                self.tag,
-                view_id,
-                self.region_id,
-                hit_bounds,
-                self.handlers.clone(),
-            )
-            .with_hoverable(self.hoverable)
-            .with_notify_on_hover(self.notify_on_hover)
-            .with_notify_on_click(self.notify_on_click),
-        );
-    }
-}
-
-impl<V: 'static> Element<V> for MouseEventHandler<V> {
-    type LayoutState = ();
-    type PaintState = ();
-
-    fn layout(
-        &mut self,
-        constraint: SizeConstraint,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> (Vector2F, Self::LayoutState) {
-        (self.child.layout(constraint, view, cx), ())
-    }
-
-    fn paint(
-        &mut self,
-        bounds: RectF,
-        visible_bounds: RectF,
-        _: &mut Self::LayoutState,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> Self::PaintState {
-        if self.above {
-            self.child.paint(bounds.origin(), visible_bounds, view, cx);
-            cx.paint_layer(None, |cx| {
-                self.paint_regions(bounds, visible_bounds, cx);
-            });
-        } else {
-            self.paint_regions(bounds, visible_bounds, cx);
-            self.child.paint(bounds.origin(), visible_bounds, view, cx);
-        }
-    }
-
-    fn rect_for_text_range(
-        &self,
-        range_utf16: Range<usize>,
-        _: RectF,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> Option<RectF> {
-        self.child.rect_for_text_range(range_utf16, view, cx)
-    }
-
-    fn debug(
-        &self,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> serde_json::Value {
-        json!({
-            "type": "MouseEventHandler",
-            "child": self.child.debug(view, cx),
-        })
-    }
-}

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

@@ -1,260 +1,241 @@
-use std::ops::Range;
+use smallvec::SmallVec;
+use taffy::style::{Display, Position};
 
 use crate::{
-    geometry::{rect::RectF, vector::Vector2F},
-    json::ToJson,
-    AnyElement, Axis, Element, MouseRegion, SizeConstraint, ViewContext,
+    point, AnyElement, BorrowWindow, Bounds, Element, IntoElement, LayoutId, ParentElement, Pixels,
+    Point, Size, Style, WindowContext,
 };
-use serde_json::json;
 
-pub struct Overlay<V> {
-    child: AnyElement<V>,
-    anchor_position: Option<Vector2F>,
-    anchor_corner: AnchorCorner,
-    fit_mode: OverlayFitMode,
-    position_mode: OverlayPositionMode,
-    hoverable: bool,
-    z_index: Option<usize>,
-}
-
-#[derive(Copy, Clone)]
-pub enum OverlayFitMode {
-    SnapToWindow,
-    SwitchAnchor,
-    None,
+pub struct OverlayState {
+    child_layout_ids: SmallVec<[LayoutId; 4]>,
 }
 
-#[derive(Copy, Clone, PartialEq, Eq)]
-pub enum OverlayPositionMode {
-    Window,
-    Local,
-}
-
-#[derive(Clone, Copy, PartialEq, Eq)]
-pub enum AnchorCorner {
-    TopLeft,
-    TopRight,
-    BottomLeft,
-    BottomRight,
+pub struct Overlay {
+    children: SmallVec<[AnyElement; 2]>,
+    anchor_corner: AnchorCorner,
+    fit_mode: OverlayFitMode,
+    // todo!();
+    anchor_position: Option<Point<Pixels>>,
+    // position_mode: OverlayPositionMode,
 }
 
-impl AnchorCorner {
-    fn get_bounds(&self, anchor_position: Vector2F, size: Vector2F) -> RectF {
-        match self {
-            Self::TopLeft => RectF::from_points(anchor_position, anchor_position + size),
-            Self::TopRight => RectF::from_points(
-                anchor_position - Vector2F::new(size.x(), 0.),
-                anchor_position + Vector2F::new(0., size.y()),
-            ),
-            Self::BottomLeft => RectF::from_points(
-                anchor_position - Vector2F::new(0., size.y()),
-                anchor_position + Vector2F::new(size.x(), 0.),
-            ),
-            Self::BottomRight => RectF::from_points(anchor_position - size, anchor_position),
-        }
-    }
-
-    fn switch_axis(self, axis: Axis) -> Self {
-        match axis {
-            Axis::Vertical => match self {
-                AnchorCorner::TopLeft => AnchorCorner::BottomLeft,
-                AnchorCorner::TopRight => AnchorCorner::BottomRight,
-                AnchorCorner::BottomLeft => AnchorCorner::TopLeft,
-                AnchorCorner::BottomRight => AnchorCorner::TopRight,
-            },
-            Axis::Horizontal => match self {
-                AnchorCorner::TopLeft => AnchorCorner::TopRight,
-                AnchorCorner::TopRight => AnchorCorner::TopLeft,
-                AnchorCorner::BottomLeft => AnchorCorner::BottomRight,
-                AnchorCorner::BottomRight => AnchorCorner::BottomLeft,
-            },
-        }
+/// overlay gives you a floating element that will avoid overflowing the window bounds.
+/// Its children should have no margin to avoid measurement issues.
+pub fn overlay() -> Overlay {
+    Overlay {
+        children: SmallVec::new(),
+        anchor_corner: AnchorCorner::TopLeft,
+        fit_mode: OverlayFitMode::SwitchAnchor,
+        anchor_position: None,
     }
 }
 
-impl<V: 'static> Overlay<V> {
-    pub fn new(child: impl Element<V>) -> Self {
-        Self {
-            child: child.into_any(),
-            anchor_position: None,
-            anchor_corner: AnchorCorner::TopLeft,
-            fit_mode: OverlayFitMode::None,
-            position_mode: OverlayPositionMode::Window,
-            hoverable: false,
-            z_index: None,
-        }
-    }
-
-    pub fn with_anchor_position(mut self, position: Vector2F) -> Self {
-        self.anchor_position = Some(position);
-        self
-    }
-
-    pub fn with_anchor_corner(mut self, anchor_corner: AnchorCorner) -> Self {
-        self.anchor_corner = anchor_corner;
-        self
-    }
-
-    pub fn with_fit_mode(mut self, fit_mode: OverlayFitMode) -> Self {
-        self.fit_mode = fit_mode;
+impl Overlay {
+    /// Sets which corner of the overlay should be anchored to the current position.
+    pub fn anchor(mut self, anchor: AnchorCorner) -> Self {
+        self.anchor_corner = anchor;
         self
     }
 
-    pub fn with_position_mode(mut self, position_mode: OverlayPositionMode) -> Self {
-        self.position_mode = position_mode;
+    /// Sets the position in window co-ordinates
+    /// (otherwise the location the overlay is rendered is used)
+    pub fn position(mut self, anchor: Point<Pixels>) -> Self {
+        self.anchor_position = Some(anchor);
         self
     }
 
-    pub fn with_hoverable(mut self, hoverable: bool) -> Self {
-        self.hoverable = hoverable;
+    /// Snap to window edge instead of switching anchor corner when an overflow would occur.
+    pub fn snap_to_window(mut self) -> Self {
+        self.fit_mode = OverlayFitMode::SnapToWindow;
         self
     }
+}
 
-    pub fn with_z_index(mut self, z_index: usize) -> Self {
-        self.z_index = Some(z_index);
-        self
+impl ParentElement for Overlay {
+    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
+        &mut self.children
     }
 }
 
-impl<V: 'static> Element<V> for Overlay<V> {
-    type LayoutState = Vector2F;
-    type PaintState = ();
+impl Element for Overlay {
+    type State = OverlayState;
 
-    fn layout(
+    fn request_layout(
         &mut self,
-        constraint: SizeConstraint,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> (Vector2F, Self::LayoutState) {
-        let constraint = if self.anchor_position.is_some() {
-            SizeConstraint::new(Vector2F::zero(), cx.window_size())
-        } else {
-            constraint
+        _: Option<Self::State>,
+        cx: &mut WindowContext,
+    ) -> (crate::LayoutId, Self::State) {
+        let child_layout_ids = self
+            .children
+            .iter_mut()
+            .map(|child| child.request_layout(cx))
+            .collect::<SmallVec<_>>();
+
+        let overlay_style = Style {
+            position: Position::Absolute,
+            display: Display::Flex,
+            ..Style::default()
         };
-        let size = self.child.layout(constraint, view, cx);
-        (Vector2F::zero(), size)
+
+        let layout_id = cx.request_layout(&overlay_style, child_layout_ids.iter().copied());
+
+        (layout_id, OverlayState { child_layout_ids })
     }
 
     fn paint(
         &mut self,
-        bounds: RectF,
-        _: RectF,
-        size: &mut Self::LayoutState,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
+        bounds: crate::Bounds<crate::Pixels>,
+        element_state: &mut Self::State,
+        cx: &mut WindowContext,
     ) {
-        let (anchor_position, mut bounds) = match self.position_mode {
-            OverlayPositionMode::Window => {
-                let anchor_position = self.anchor_position.unwrap_or_else(|| bounds.origin());
-                let bounds = self.anchor_corner.get_bounds(anchor_position, *size);
-                (anchor_position, bounds)
-            }
-            OverlayPositionMode::Local => {
-                let anchor_position = self.anchor_position.unwrap_or_default();
-                let bounds = self
-                    .anchor_corner
-                    .get_bounds(bounds.origin() + anchor_position, *size);
-                (anchor_position, bounds)
-            }
+        if element_state.child_layout_ids.is_empty() {
+            return;
+        }
+
+        let mut child_min = point(Pixels::MAX, Pixels::MAX);
+        let mut child_max = Point::default();
+        for child_layout_id in &element_state.child_layout_ids {
+            let child_bounds = cx.layout_bounds(*child_layout_id);
+            child_min = child_min.min(&child_bounds.origin);
+            child_max = child_max.max(&child_bounds.lower_right());
+        }
+        let size: Size<Pixels> = (child_max - child_min).into();
+        let origin = self.anchor_position.unwrap_or(bounds.origin);
+
+        let mut desired = self.anchor_corner.get_bounds(origin, size);
+        let limits = Bounds {
+            origin: Point::default(),
+            size: cx.viewport_size(),
         };
 
-        match self.fit_mode {
-            OverlayFitMode::SnapToWindow => {
-                // Snap the horizontal edges of the overlay to the horizontal edges of the window if
-                // its horizontal bounds overflow
-                if bounds.max_x() > cx.window_size().x() {
-                    let mut lower_right = bounds.lower_right();
-                    lower_right.set_x(cx.window_size().x());
-                    bounds = RectF::from_points(lower_right - *size, lower_right);
-                } else if bounds.min_x() < 0. {
-                    let mut upper_left = bounds.origin();
-                    upper_left.set_x(0.);
-                    bounds = RectF::from_points(upper_left, upper_left + *size);
-                }
+        if self.fit_mode == OverlayFitMode::SwitchAnchor {
+            let mut anchor_corner = self.anchor_corner;
 
-                // Snap the vertical edges of the overlay to the vertical edges of the window if
-                // its vertical bounds overflow.
-                if bounds.max_y() > cx.window_size().y() {
-                    let mut lower_right = bounds.lower_right();
-                    lower_right.set_y(cx.window_size().y());
-                    bounds = RectF::from_points(lower_right - *size, lower_right);
-                } else if bounds.min_y() < 0. {
-                    let mut upper_left = bounds.origin();
-                    upper_left.set_y(0.);
-                    bounds = RectF::from_points(upper_left, upper_left + *size);
+            if desired.left() < limits.left() || desired.right() > limits.right() {
+                let switched = anchor_corner
+                    .switch_axis(Axis::Horizontal)
+                    .get_bounds(origin, size);
+                if !(switched.left() < limits.left() || switched.right() > limits.right()) {
+                    anchor_corner = anchor_corner.switch_axis(Axis::Horizontal);
+                    desired = switched
                 }
             }
-            OverlayFitMode::SwitchAnchor => {
-                let mut anchor_corner = self.anchor_corner;
 
-                if bounds.max_x() > cx.window_size().x() {
-                    anchor_corner = anchor_corner.switch_axis(Axis::Horizontal);
+            if desired.top() < limits.top() || desired.bottom() > limits.bottom() {
+                let switched = anchor_corner
+                    .switch_axis(Axis::Vertical)
+                    .get_bounds(origin, size);
+                if !(switched.top() < limits.top() || switched.bottom() > limits.bottom()) {
+                    desired = switched;
                 }
+            }
+        }
 
-                if bounds.max_y() > cx.window_size().y() {
-                    anchor_corner = anchor_corner.switch_axis(Axis::Vertical);
-                }
+        // Snap the horizontal edges of the overlay to the horizontal edges of the window if
+        // its horizontal bounds overflow, aligning to the left if it is wider than the limits.
+        if desired.right() > limits.right() {
+            desired.origin.x -= desired.right() - limits.right();
+        }
+        if desired.left() < limits.left() {
+            desired.origin.x = limits.origin.x;
+        }
 
-                if bounds.min_x() < 0. {
-                    anchor_corner = anchor_corner.switch_axis(Axis::Horizontal)
-                }
+        // Snap the vertical edges of the overlay to the vertical edges of the window if
+        // its vertical bounds overflow, aligning to the top if it is taller than the limits.
+        if desired.bottom() > limits.bottom() {
+            desired.origin.y -= desired.bottom() - limits.bottom();
+        }
+        if desired.top() < limits.top() {
+            desired.origin.y = limits.origin.y;
+        }
 
-                if bounds.min_y() < 0. {
-                    anchor_corner = anchor_corner.switch_axis(Axis::Vertical)
+        let mut offset = cx.element_offset() + desired.origin - bounds.origin;
+        offset = point(offset.x.round(), offset.y.round());
+        cx.with_absolute_element_offset(offset, |cx| {
+            cx.break_content_mask(|cx| {
+                for child in &mut self.children {
+                    child.paint(cx);
                 }
+            })
+        })
+    }
+}
 
-                // Update bounds if needed
-                if anchor_corner != self.anchor_corner {
-                    bounds = anchor_corner.get_bounds(anchor_position, *size)
-                }
-            }
-            OverlayFitMode::None => {}
-        }
+impl IntoElement for Overlay {
+    type Element = Self;
 
-        cx.scene().push_stacking_context(None, self.z_index);
-        if self.hoverable {
-            enum OverlayHoverCapture {}
-            // Block hovers in lower stacking contexts
-            let view_id = cx.view_id();
-            cx.scene()
-                .push_mouse_region(MouseRegion::new::<OverlayHoverCapture>(
-                    view_id, view_id, bounds,
-                ));
-        }
-        self.child.paint(
-            bounds.origin(),
-            RectF::new(Vector2F::zero(), cx.window_size()),
-            view,
-            cx,
-        );
-        cx.scene().pop_stacking_context();
+    fn element_id(&self) -> Option<crate::ElementId> {
+        None
     }
 
-    fn rect_for_text_range(
-        &self,
-        range_utf16: Range<usize>,
-        _: RectF,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> Option<RectF> {
-        self.child.rect_for_text_range(range_utf16, view, cx)
+    fn into_element(self) -> Self::Element {
+        self
     }
+}
 
-    fn debug(
-        &self,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> serde_json::Value {
-        json!({
-            "type": "Overlay",
-            "abs_position": self.anchor_position.to_json(),
-            "child": self.child.debug(view, cx),
-        })
+enum Axis {
+    Horizontal,
+    Vertical,
+}
+
+#[derive(Copy, Clone, PartialEq)]
+pub enum OverlayFitMode {
+    SnapToWindow,
+    SwitchAnchor,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub enum AnchorCorner {
+    TopLeft,
+    TopRight,
+    BottomLeft,
+    BottomRight,
+}
+
+impl AnchorCorner {
+    fn get_bounds(&self, origin: Point<Pixels>, size: Size<Pixels>) -> Bounds<Pixels> {
+        let origin = match self {
+            Self::TopLeft => origin,
+            Self::TopRight => Point {
+                x: origin.x - size.width,
+                y: origin.y,
+            },
+            Self::BottomLeft => Point {
+                x: origin.x,
+                y: origin.y - size.height,
+            },
+            Self::BottomRight => Point {
+                x: origin.x - size.width,
+                y: origin.y - size.height,
+            },
+        };
+
+        Bounds { origin, size }
+    }
+
+    pub fn corner(&self, bounds: Bounds<Pixels>) -> Point<Pixels> {
+        match self {
+            Self::TopLeft => bounds.origin,
+            Self::TopRight => bounds.upper_right(),
+            Self::BottomLeft => bounds.lower_left(),
+            Self::BottomRight => bounds.lower_right(),
+        }
+    }
+
+    fn switch_axis(self, axis: Axis) -> Self {
+        match axis {
+            Axis::Vertical => match self {
+                AnchorCorner::TopLeft => AnchorCorner::BottomLeft,
+                AnchorCorner::TopRight => AnchorCorner::BottomRight,
+                AnchorCorner::BottomLeft => AnchorCorner::TopLeft,
+                AnchorCorner::BottomRight => AnchorCorner::TopRight,
+            },
+            Axis::Horizontal => match self {
+                AnchorCorner::TopLeft => AnchorCorner::TopRight,
+                AnchorCorner::TopRight => AnchorCorner::TopLeft,
+                AnchorCorner::BottomLeft => AnchorCorner::BottomRight,
+                AnchorCorner::BottomRight => AnchorCorner::BottomLeft,
+            },
+        }
     }
 }

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

@@ -1,290 +0,0 @@
-use std::{cell::RefCell, rc::Rc};
-
-use collections::HashMap;
-use pathfinder_geometry::vector::{vec2f, Vector2F};
-use serde_json::json;
-
-use crate::{
-    geometry::rect::RectF,
-    platform::{CursorStyle, MouseButton},
-    AnyElement, AppContext, Axis, Element, MouseRegion, SizeConstraint, TypeTag, View, ViewContext,
-};
-
-#[derive(Copy, Clone, Debug)]
-pub enum HandleSide {
-    Top,
-    Bottom,
-    Left,
-    Right,
-}
-
-impl HandleSide {
-    fn axis(&self) -> Axis {
-        match self {
-            HandleSide::Left | HandleSide::Right => Axis::Horizontal,
-            HandleSide::Top | HandleSide::Bottom => Axis::Vertical,
-        }
-    }
-
-    fn relevant_component(&self, vector: Vector2F) -> f32 {
-        match self.axis() {
-            Axis::Horizontal => vector.x(),
-            Axis::Vertical => vector.y(),
-        }
-    }
-
-    fn of_rect(&self, bounds: RectF, handle_size: f32) -> RectF {
-        match self {
-            HandleSide::Top => RectF::new(bounds.origin(), vec2f(bounds.width(), handle_size)),
-            HandleSide::Left => RectF::new(bounds.origin(), vec2f(handle_size, bounds.height())),
-            HandleSide::Bottom => {
-                let mut origin = bounds.lower_left();
-                origin.set_y(origin.y() - handle_size);
-                RectF::new(origin, vec2f(bounds.width(), handle_size))
-            }
-            HandleSide::Right => {
-                let mut origin = bounds.upper_right();
-                origin.set_x(origin.x() - handle_size);
-                RectF::new(origin, vec2f(handle_size, bounds.height()))
-            }
-        }
-    }
-}
-
-fn get_bounds(tag: TypeTag, cx: &AppContext) -> Option<&(RectF, RectF)>
-where
-{
-    cx.optional_global::<ProviderMap>()
-        .and_then(|map| map.0.get(&tag))
-}
-
-pub struct Resizable<V: 'static> {
-    child: AnyElement<V>,
-    tag: TypeTag,
-    handle_side: HandleSide,
-    handle_size: f32,
-    on_resize: Rc<RefCell<dyn FnMut(&mut V, Option<f32>, &mut ViewContext<V>)>>,
-}
-
-const DEFAULT_HANDLE_SIZE: f32 = 4.0;
-
-impl<V: 'static> Resizable<V> {
-    pub fn new<Tag: 'static>(
-        child: AnyElement<V>,
-        handle_side: HandleSide,
-        size: f32,
-        on_resize: impl 'static + FnMut(&mut V, Option<f32>, &mut ViewContext<V>),
-    ) -> Self {
-        let child = match handle_side.axis() {
-            Axis::Horizontal => child.constrained().with_max_width(size),
-            Axis::Vertical => child.constrained().with_max_height(size),
-        }
-        .into_any();
-
-        Self {
-            child,
-            handle_side,
-            tag: TypeTag::new::<Tag>(),
-            handle_size: DEFAULT_HANDLE_SIZE,
-            on_resize: Rc::new(RefCell::new(on_resize)),
-        }
-    }
-
-    pub fn with_handle_size(mut self, handle_size: f32) -> Self {
-        self.handle_size = handle_size;
-        self
-    }
-}
-
-impl<V: 'static> Element<V> for Resizable<V> {
-    type LayoutState = SizeConstraint;
-    type PaintState = ();
-
-    fn layout(
-        &mut self,
-        constraint: crate::SizeConstraint,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> (Vector2F, Self::LayoutState) {
-        (self.child.layout(constraint, view, cx), constraint)
-    }
-
-    fn paint(
-        &mut self,
-        bounds: pathfinder_geometry::rect::RectF,
-        visible_bounds: pathfinder_geometry::rect::RectF,
-        constraint: &mut SizeConstraint,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> Self::PaintState {
-        cx.scene().push_stacking_context(None, None);
-
-        let handle_region = self.handle_side.of_rect(bounds, self.handle_size);
-
-        enum ResizeHandle {}
-        let view_id = cx.view_id();
-        cx.scene().push_mouse_region(
-            MouseRegion::new::<ResizeHandle>(view_id, self.handle_side as usize, handle_region)
-                .on_down(MouseButton::Left, |_, _: &mut V, _| {}) // This prevents the mouse down event from being propagated elsewhere
-                .on_click(MouseButton::Left, {
-                    let on_resize = self.on_resize.clone();
-                    move |click, v, cx| {
-                        if click.click_count == 2 {
-                            on_resize.borrow_mut()(v, None, cx);
-                        }
-                    }
-                })
-                .on_drag(MouseButton::Left, {
-                    let bounds = bounds.clone();
-                    let side = self.handle_side;
-                    let prev_size = side.relevant_component(bounds.size());
-                    let min_size = side.relevant_component(constraint.min);
-                    let max_size = side.relevant_component(constraint.max);
-                    let on_resize = self.on_resize.clone();
-                    let tag = self.tag;
-                    move |event, view: &mut V, cx| {
-                        if event.end {
-                            return;
-                        }
-
-                        let Some((bounds, _)) = get_bounds(tag, cx) else {
-                            return;
-                        };
-
-                        let new_size_raw = match side {
-                            // Handle on top side of element => Element is on bottom
-                            HandleSide::Top => {
-                                bounds.height() + bounds.origin_y() - event.position.y()
-                            }
-                            // Handle on right side of element => Element is on left
-                            HandleSide::Right => event.position.x() - bounds.lower_left().x(),
-                            // Handle on left side of element => Element is on the right
-                            HandleSide::Left => {
-                                bounds.width() + bounds.origin_x() - event.position.x()
-                            }
-                            // Handle on bottom side of element => Element is on the top
-                            HandleSide::Bottom => event.position.y() - bounds.lower_left().y(),
-                        };
-
-                        let new_size = min_size.max(new_size_raw).min(max_size).round();
-                        if new_size != prev_size {
-                            on_resize.borrow_mut()(view, Some(new_size), cx);
-                        }
-                    }
-                }),
-        );
-
-        cx.scene().push_cursor_region(crate::CursorRegion {
-            bounds: handle_region,
-            style: match self.handle_side.axis() {
-                Axis::Horizontal => CursorStyle::ResizeLeftRight,
-                Axis::Vertical => CursorStyle::ResizeUpDown,
-            },
-        });
-
-        cx.scene().pop_stacking_context();
-
-        self.child.paint(bounds.origin(), visible_bounds, view, cx);
-    }
-
-    fn rect_for_text_range(
-        &self,
-        range_utf16: std::ops::Range<usize>,
-        _bounds: pathfinder_geometry::rect::RectF,
-        _visible_bounds: pathfinder_geometry::rect::RectF,
-        _layout: &Self::LayoutState,
-        _paint: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> Option<pathfinder_geometry::rect::RectF> {
-        self.child.rect_for_text_range(range_utf16, view, cx)
-    }
-
-    fn debug(
-        &self,
-        _bounds: pathfinder_geometry::rect::RectF,
-        _layout: &Self::LayoutState,
-        _paint: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> serde_json::Value {
-        json!({
-            "child": self.child.debug(view, cx),
-        })
-    }
-}
-
-#[derive(Debug, Default)]
-struct ProviderMap(HashMap<TypeTag, (RectF, RectF)>);
-
-pub struct BoundsProvider<V: 'static, P> {
-    child: AnyElement<V>,
-    phantom: std::marker::PhantomData<P>,
-}
-
-impl<V: 'static, P: 'static> BoundsProvider<V, P> {
-    pub fn new(child: AnyElement<V>) -> Self {
-        Self {
-            child,
-            phantom: std::marker::PhantomData,
-        }
-    }
-}
-
-impl<V: View, P: 'static> Element<V> for BoundsProvider<V, P> {
-    type LayoutState = ();
-
-    type PaintState = ();
-
-    fn layout(
-        &mut self,
-        constraint: crate::SizeConstraint,
-        view: &mut V,
-        cx: &mut crate::ViewContext<V>,
-    ) -> (pathfinder_geometry::vector::Vector2F, Self::LayoutState) {
-        (self.child.layout(constraint, view, cx), ())
-    }
-
-    fn paint(
-        &mut self,
-        bounds: pathfinder_geometry::rect::RectF,
-        visible_bounds: pathfinder_geometry::rect::RectF,
-        _: &mut Self::LayoutState,
-        view: &mut V,
-        cx: &mut crate::ViewContext<V>,
-    ) -> Self::PaintState {
-        cx.update_default_global::<ProviderMap, _, _>(|map, _| {
-            map.0.insert(TypeTag::new::<P>(), (bounds, visible_bounds));
-        });
-
-        self.child.paint(bounds.origin(), visible_bounds, view, cx)
-    }
-
-    fn rect_for_text_range(
-        &self,
-        range_utf16: std::ops::Range<usize>,
-        _: pathfinder_geometry::rect::RectF,
-        _: pathfinder_geometry::rect::RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &crate::ViewContext<V>,
-    ) -> Option<pathfinder_geometry::rect::RectF> {
-        self.child.rect_for_text_range(range_utf16, view, cx)
-    }
-
-    fn debug(
-        &self,
-        _: pathfinder_geometry::rect::RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &crate::ViewContext<V>,
-    ) -> serde_json::Value {
-        serde_json::json!({
-            "type": "Provider",
-            "providing": format!("{:?}", TypeTag::new::<P>()),
-            "child": self.child.debug(view, cx),
-        })
-    }
-}

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

@@ -1,104 +0,0 @@
-use std::ops::Range;
-
-use crate::{
-    geometry::{rect::RectF, vector::Vector2F},
-    json::{self, json, ToJson},
-    AnyElement, Element, SizeConstraint, ViewContext,
-};
-
-/// Element which renders it's children in a stack on top of each other.
-/// The first child determines the size of the others.
-pub struct Stack<V> {
-    children: Vec<AnyElement<V>>,
-}
-
-impl<V> Default for Stack<V> {
-    fn default() -> Self {
-        Self {
-            children: Vec::new(),
-        }
-    }
-}
-
-impl<V> Stack<V> {
-    pub fn new() -> Self {
-        Self::default()
-    }
-}
-
-impl<V: 'static> Element<V> for Stack<V> {
-    type LayoutState = ();
-    type PaintState = ();
-
-    fn layout(
-        &mut self,
-        mut constraint: SizeConstraint,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> (Vector2F, Self::LayoutState) {
-        let mut size = constraint.min;
-        let mut children = self.children.iter_mut();
-        if let Some(bottom_child) = children.next() {
-            size = bottom_child.layout(constraint, view, cx);
-            constraint = SizeConstraint::strict(size);
-        }
-
-        for child in children {
-            child.layout(constraint, view, cx);
-        }
-
-        (size, ())
-    }
-
-    fn paint(
-        &mut self,
-        bounds: RectF,
-        visible_bounds: RectF,
-        _: &mut Self::LayoutState,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> Self::PaintState {
-        for child in &mut self.children {
-            cx.scene().push_layer(None);
-            child.paint(bounds.origin(), visible_bounds, view, cx);
-            cx.scene().pop_layer();
-        }
-    }
-
-    fn rect_for_text_range(
-        &self,
-        range_utf16: Range<usize>,
-        _: RectF,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> Option<RectF> {
-        self.children
-            .iter()
-            .rev()
-            .find_map(|child| child.rect_for_text_range(range_utf16.clone(), view, cx))
-    }
-
-    fn debug(
-        &self,
-        bounds: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> json::Value {
-        json!({
-            "type": "Stack",
-            "bounds": bounds.to_json(),
-            "children": self.children.iter().map(|child| child.debug(view, cx)).collect::<Vec<json::Value>>()
-        })
-    }
-}
-
-impl<V> Extend<AnyElement<V>> for Stack<V> {
-    fn extend<T: IntoIterator<Item = AnyElement<V>>>(&mut self, children: T) {
-        self.children.extend(children)
-    }
-}

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

@@ -1,141 +1,78 @@
-use super::constrain_size_preserving_aspect_ratio;
-use crate::json::ToJson;
 use crate::{
-    color::Color,
-    geometry::{
-        rect::RectF,
-        vector::{vec2f, Vector2F},
-    },
-    scene, Element, SizeConstraint, ViewContext,
+    Bounds, Element, ElementId, InteractiveElement, InteractiveElementState, Interactivity,
+    IntoElement, LayoutId, Pixels, SharedString, StyleRefinement, Styled, WindowContext,
 };
-use schemars::JsonSchema;
-use serde_derive::Deserialize;
-use serde_json::json;
-use std::{borrow::Cow, ops::Range};
+use util::ResultExt;
 
 pub struct Svg {
-    path: Cow<'static, str>,
-    color: Color,
+    interactivity: Interactivity,
+    path: Option<SharedString>,
 }
 
-impl Svg {
-    pub fn new(path: impl Into<Cow<'static, str>>) -> Self {
-        Self {
-            path: path.into(),
-            color: Color::black(),
-        }
-    }
-
-    pub fn for_style<V: 'static>(style: SvgStyle) -> impl Element<V> {
-        Self::new(style.asset)
-            .with_color(style.color)
-            .constrained()
-            .with_width(style.dimensions.width)
-            .with_height(style.dimensions.height)
+pub fn svg() -> Svg {
+    Svg {
+        interactivity: Interactivity::default(),
+        path: None,
     }
+}
 
-    pub fn with_color(mut self, color: Color) -> Self {
-        self.color = color;
+impl Svg {
+    pub fn path(mut self, path: impl Into<SharedString>) -> Self {
+        self.path = Some(path.into());
         self
     }
 }
 
-impl<V: 'static> Element<V> for Svg {
-    type LayoutState = Option<usvg::Tree>;
-    type PaintState = ();
+impl Element for Svg {
+    type State = InteractiveElementState;
 
-    fn layout(
+    fn request_layout(
         &mut self,
-        constraint: SizeConstraint,
-        _: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> (Vector2F, Self::LayoutState) {
-        match cx.asset_cache.svg(&self.path) {
-            Ok(tree) => {
-                let size = constrain_size_preserving_aspect_ratio(
-                    constraint.max,
-                    from_usvg_rect(tree.svg_node().view_box.rect).size(),
-                );
-                (size, Some(tree))
-            }
-            Err(_error) => {
-                #[cfg(not(any(test, feature = "test-support")))]
-                log::error!("{}", _error);
-                (constraint.min, None)
-            }
-        }
+        element_state: Option<Self::State>,
+        cx: &mut WindowContext,
+    ) -> (LayoutId, Self::State) {
+        self.interactivity.layout(element_state, cx, |style, cx| {
+            cx.request_layout(&style, None)
+        })
     }
 
     fn paint(
         &mut self,
-        bounds: RectF,
-        _visible_bounds: RectF,
-        svg: &mut Self::LayoutState,
-        _: &mut V,
-        cx: &mut ViewContext<V>,
-    ) {
-        if let Some(svg) = svg.clone() {
-            cx.scene().push_icon(scene::Icon {
-                bounds,
-                svg,
-                path: self.path.clone(),
-                color: self.color,
-            });
-        }
+        bounds: Bounds<Pixels>,
+        element_state: &mut Self::State,
+        cx: &mut WindowContext,
+    ) where
+        Self: Sized,
+    {
+        self.interactivity
+            .paint(bounds, bounds.size, element_state, cx, |style, _, cx| {
+                if let Some((path, color)) = self.path.as_ref().zip(style.text.color) {
+                    cx.paint_svg(bounds, path.clone(), color).log_err();
+                }
+            })
     }
+}
 
-    fn rect_for_text_range(
-        &self,
-        _: Range<usize>,
-        _: RectF,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        _: &V,
-        _: &ViewContext<V>,
-    ) -> Option<RectF> {
-        None
-    }
+impl IntoElement for Svg {
+    type Element = Self;
 
-    fn debug(
-        &self,
-        bounds: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        _: &V,
-        _: &ViewContext<V>,
-    ) -> serde_json::Value {
-        json!({
-            "type": "Svg",
-            "bounds": bounds.to_json(),
-            "path": self.path,
-            "color": self.color.to_json(),
-        })
+    fn element_id(&self) -> Option<ElementId> {
+        self.interactivity.element_id.clone()
     }
-}
-
-#[derive(Clone, Deserialize, Default, JsonSchema)]
-pub struct SvgStyle {
-    pub color: Color,
-    pub asset: String,
-    pub dimensions: Dimensions,
-}
 
-#[derive(Clone, Deserialize, Default, JsonSchema)]
-pub struct Dimensions {
-    pub width: f32,
-    pub height: f32,
+    fn into_element(self) -> Self::Element {
+        self
+    }
 }
 
-impl Dimensions {
-    pub fn to_vec(&self) -> Vector2F {
-        vec2f(self.width, self.height)
+impl Styled for Svg {
+    fn style(&mut self) -> &mut StyleRefinement {
+        &mut self.interactivity.base_style
     }
 }
 
-fn from_usvg_rect(rect: usvg::Rect) -> RectF {
-    RectF::new(
-        vec2f(rect.x() as f32, rect.y() as f32),
-        vec2f(rect.width() as f32, rect.height() as f32),
-    )
+impl InteractiveElement for Svg {
+    fn interactivity(&mut self) -> &mut Interactivity {
+        &mut self.interactivity
+    }
 }

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

@@ -1,438 +1,423 @@
 use crate::{
-    color::Color,
-    fonts::{HighlightStyle, TextStyle},
-    geometry::{
-        rect::RectF,
-        vector::{vec2f, Vector2F},
-    },
-    json::{ToJson, Value},
-    text_layout::{Line, RunStyle, ShapedBoundary},
-    Element, FontCache, SizeConstraint, TextLayoutCache, ViewContext, WindowContext,
+    Bounds, DispatchPhase, Element, ElementId, HighlightStyle, IntoElement, LayoutId,
+    MouseDownEvent, MouseUpEvent, Pixels, Point, SharedString, Size, TextRun, TextStyle,
+    WhiteSpace, WindowContext, WrappedLine,
 };
-use log::warn;
-use serde_json::json;
-use std::{borrow::Cow, ops::Range, sync::Arc};
-
-pub struct Text {
-    text: Cow<'static, str>,
-    style: TextStyle,
-    soft_wrap: bool,
-    highlights: Option<Box<[(Range<usize>, HighlightStyle)]>>,
-    custom_runs: Option<(
-        Box<[Range<usize>]>,
-        Box<dyn FnMut(usize, RectF, &mut WindowContext)>,
-    )>,
+use anyhow::anyhow;
+use parking_lot::{Mutex, MutexGuard};
+use smallvec::SmallVec;
+use std::{cell::Cell, mem, ops::Range, rc::Rc, sync::Arc};
+use util::ResultExt;
+
+impl Element for &'static str {
+    type State = TextState;
+
+    fn request_layout(
+        &mut self,
+        _: Option<Self::State>,
+        cx: &mut WindowContext,
+    ) -> (LayoutId, Self::State) {
+        let mut state = TextState::default();
+        let layout_id = state.layout(SharedString::from(*self), None, cx);
+        (layout_id, state)
+    }
+
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut WindowContext) {
+        state.paint(bounds, self, cx)
+    }
 }
 
-pub struct LayoutState {
-    shaped_lines: Vec<Line>,
-    wrap_boundaries: Vec<Vec<ShapedBoundary>>,
-    line_height: f32,
+impl IntoElement for &'static str {
+    type Element = Self;
+
+    fn element_id(&self) -> Option<ElementId> {
+        None
+    }
+
+    fn into_element(self) -> Self::Element {
+        self
+    }
 }
 
-impl Text {
-    pub fn new<I: Into<Cow<'static, str>>>(text: I, style: TextStyle) -> Self {
-        Self {
-            text: text.into(),
-            style,
-            soft_wrap: true,
-            highlights: None,
-            custom_runs: None,
-        }
+impl Element for SharedString {
+    type State = TextState;
+
+    fn request_layout(
+        &mut self,
+        _: Option<Self::State>,
+        cx: &mut WindowContext,
+    ) -> (LayoutId, Self::State) {
+        let mut state = TextState::default();
+        let layout_id = state.layout(self.clone(), None, cx);
+        (layout_id, state)
     }
 
-    pub fn with_default_color(mut self, color: Color) -> Self {
-        self.style.color = color;
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut WindowContext) {
+        let text_str: &str = self.as_ref();
+        state.paint(bounds, text_str, cx)
+    }
+}
+
+impl IntoElement for SharedString {
+    type Element = Self;
+
+    fn element_id(&self) -> Option<ElementId> {
+        None
+    }
+
+    fn into_element(self) -> Self::Element {
         self
     }
+}
+
+/// Renders text with runs of different styles.
+///
+/// Callers are responsible for setting the correct style for each run.
+/// For text with a uniform style, you can usually avoid calling this constructor
+/// and just pass text directly.
+pub struct StyledText {
+    text: SharedString,
+    runs: Option<Vec<TextRun>>,
+}
+
+impl StyledText {
+    pub fn new(text: impl Into<SharedString>) -> Self {
+        StyledText {
+            text: text.into(),
+            runs: None,
+        }
+    }
 
     pub fn with_highlights(
         mut self,
-        runs: impl Into<Box<[(Range<usize>, HighlightStyle)]>>,
+        default_style: &TextStyle,
+        highlights: impl IntoIterator<Item = (Range<usize>, HighlightStyle)>,
     ) -> Self {
-        self.highlights = Some(runs.into());
+        let mut runs = Vec::new();
+        let mut ix = 0;
+        for (range, highlight) in highlights {
+            if ix < range.start {
+                runs.push(default_style.clone().to_run(range.start - ix));
+            }
+            runs.push(
+                default_style
+                    .clone()
+                    .highlight(highlight)
+                    .to_run(range.len()),
+            );
+            ix = range.end;
+        }
+        if ix < self.text.len() {
+            runs.push(default_style.to_run(self.text.len() - ix));
+        }
+        self.runs = Some(runs);
         self
     }
+}
 
-    pub fn with_custom_runs(
-        mut self,
-        runs: impl Into<Box<[Range<usize>]>>,
-        callback: impl 'static + FnMut(usize, RectF, &mut WindowContext),
-    ) -> Self {
-        self.custom_runs = Some((runs.into(), Box::new(callback)));
-        self
+impl Element for StyledText {
+    type State = TextState;
+
+    fn request_layout(
+        &mut self,
+        _: Option<Self::State>,
+        cx: &mut WindowContext,
+    ) -> (LayoutId, Self::State) {
+        let mut state = TextState::default();
+        let layout_id = state.layout(self.text.clone(), self.runs.take(), cx);
+        (layout_id, state)
     }
 
-    pub fn with_soft_wrap(mut self, soft_wrap: bool) -> Self {
-        self.soft_wrap = soft_wrap;
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
+        state.paint(bounds, &self.text, cx)
+    }
+}
+
+impl IntoElement for StyledText {
+    type Element = Self;
+
+    fn element_id(&self) -> Option<crate::ElementId> {
+        None
+    }
+
+    fn into_element(self) -> Self::Element {
         self
     }
 }
 
-impl<V: 'static> Element<V> for Text {
-    type LayoutState = LayoutState;
-    type PaintState = ();
+#[derive(Default, Clone)]
+pub struct TextState(Arc<Mutex<Option<TextStateInner>>>);
+
+struct TextStateInner {
+    lines: SmallVec<[WrappedLine; 1]>,
+    line_height: Pixels,
+    wrap_width: Option<Pixels>,
+    size: Option<Size<Pixels>>,
+}
+
+impl TextState {
+    fn lock(&self) -> MutexGuard<Option<TextStateInner>> {
+        self.0.lock()
+    }
 
     fn layout(
         &mut self,
-        constraint: SizeConstraint,
-        _: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> (Vector2F, Self::LayoutState) {
-        // Convert the string and highlight ranges into an iterator of highlighted chunks.
-
-        let mut offset = 0;
-        let mut highlight_ranges = self
-            .highlights
-            .as_ref()
-            .map_or(Default::default(), AsRef::as_ref)
-            .iter()
-            .peekable();
-        let chunks = std::iter::from_fn(|| {
-            let result;
-            if let Some((range, highlight_style)) = highlight_ranges.peek() {
-                if offset < range.start {
-                    result = Some((&self.text[offset..range.start], None));
-                    offset = range.start;
-                } else if range.end <= self.text.len() {
-                    result = Some((&self.text[range.clone()], Some(*highlight_style)));
-                    highlight_ranges.next();
-                    offset = range.end;
+        text: SharedString,
+        runs: Option<Vec<TextRun>>,
+        cx: &mut WindowContext,
+    ) -> LayoutId {
+        let text_style = cx.text_style();
+        let font_size = text_style.font_size.to_pixels(cx.rem_size());
+        let line_height = text_style
+            .line_height
+            .to_pixels(font_size.into(), cx.rem_size());
+
+        let runs = if let Some(runs) = runs {
+            runs
+        } else {
+            vec![text_style.to_run(text.len())]
+        };
+
+        let layout_id = cx.request_measured_layout(Default::default(), {
+            let element_state = self.clone();
+
+            move |known_dimensions, available_space, cx| {
+                let wrap_width = if text_style.white_space == WhiteSpace::Normal {
+                    known_dimensions.width.or(match available_space.width {
+                        crate::AvailableSpace::Definite(x) => Some(x),
+                        _ => None,
+                    })
                 } else {
-                    warn!(
-                        "Highlight out of text range. Text len: {}, Highlight range: {}..{}",
-                        self.text.len(),
-                        range.start,
-                        range.end
-                    );
-                    result = None;
+                    None
+                };
+
+                if let Some(text_state) = element_state.0.lock().as_ref() {
+                    if text_state.size.is_some()
+                        && (wrap_width.is_none() || wrap_width == text_state.wrap_width)
+                    {
+                        return text_state.size.unwrap();
+                    }
                 }
-            } else if offset < self.text.len() {
-                result = Some((&self.text[offset..], None));
-                offset = self.text.len();
-            } else {
-                result = None;
+
+                let Some(lines) = cx
+                    .text_system()
+                    .shape_text(
+                        &text, font_size, &runs, wrap_width, // Wrap if we know the width.
+                    )
+                    .log_err()
+                else {
+                    element_state.lock().replace(TextStateInner {
+                        lines: Default::default(),
+                        line_height,
+                        wrap_width,
+                        size: Some(Size::default()),
+                    });
+                    return Size::default();
+                };
+
+                let mut size: Size<Pixels> = Size::default();
+                for line in &lines {
+                    let line_size = line.size(line_height);
+                    size.height += line_size.height;
+                    size.width = size.width.max(line_size.width).ceil();
+                }
+
+                element_state.lock().replace(TextStateInner {
+                    lines,
+                    line_height,
+                    wrap_width,
+                    size: Some(size),
+                });
+
+                size
             }
-            result
         });
 
-        // Perform shaping on these highlighted chunks
-        let shaped_lines = layout_highlighted_chunks(
-            chunks,
-            &self.style,
-            cx.text_layout_cache(),
-            &cx.font_cache,
-            usize::MAX,
-            self.text.matches('\n').count() + 1,
-        );
-
-        // If line wrapping is enabled, wrap each of the shaped lines.
-        let font_id = self.style.font_id;
-        let mut line_count = 0;
-        let mut max_line_width = 0_f32;
-        let mut wrap_boundaries = Vec::new();
-        let mut wrapper = cx.font_cache.line_wrapper(font_id, self.style.font_size);
-        for (line, shaped_line) in self.text.split('\n').zip(&shaped_lines) {
-            if self.soft_wrap {
-                let boundaries = wrapper
-                    .wrap_shaped_line(line, shaped_line, constraint.max.x())
-                    .collect::<Vec<_>>();
-                line_count += boundaries.len() + 1;
-                wrap_boundaries.push(boundaries);
-            } else {
-                line_count += 1;
-            }
-            max_line_width = max_line_width.max(shaped_line.width());
-        }
+        layout_id
+    }
 
-        let line_height = cx.font_cache.line_height(self.style.font_size);
-        let size = vec2f(
-            max_line_width
-                .ceil()
-                .max(constraint.min.x())
-                .min(constraint.max.x()),
-            (line_height * line_count as f32).ceil(),
-        );
-        (
-            size,
-            LayoutState {
-                shaped_lines,
-                wrap_boundaries,
-                line_height,
-            },
-        )
+    fn paint(&mut self, bounds: Bounds<Pixels>, text: &str, cx: &mut WindowContext) {
+        let element_state = self.lock();
+        let element_state = element_state
+            .as_ref()
+            .ok_or_else(|| anyhow!("measurement has not been performed on {}", text))
+            .unwrap();
+
+        let line_height = element_state.line_height;
+        let mut line_origin = bounds.origin;
+        for line in &element_state.lines {
+            line.paint(line_origin, line_height, cx).log_err();
+            line_origin.y += line.size(line_height).height;
+        }
     }
 
-    fn paint(
-        &mut self,
-        bounds: RectF,
-        visible_bounds: RectF,
-        layout: &mut Self::LayoutState,
-        _: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> Self::PaintState {
-        let mut origin = bounds.origin();
-        let empty = Vec::new();
-        let mut callback = |_, _, _: &mut WindowContext| {};
-
-        let mouse_runs;
-        let custom_run_callback;
-        if let Some((runs, build_region)) = &mut self.custom_runs {
-            mouse_runs = runs.iter();
-            custom_run_callback = build_region.as_mut();
-        } else {
-            mouse_runs = [].iter();
-            custom_run_callback = &mut callback;
+    fn index_for_position(&self, bounds: Bounds<Pixels>, position: Point<Pixels>) -> Option<usize> {
+        if !bounds.contains(&position) {
+            return None;
         }
-        let mut custom_runs = mouse_runs.enumerate().peekable();
-
-        let mut offset = 0;
-        for (ix, line) in layout.shaped_lines.iter().enumerate() {
-            let wrap_boundaries = layout.wrap_boundaries.get(ix).unwrap_or(&empty);
-            let boundaries = RectF::new(
-                origin,
-                vec2f(
-                    bounds.width(),
-                    (wrap_boundaries.len() + 1) as f32 * layout.line_height,
-                ),
-            );
 
-            if boundaries.intersects(visible_bounds) {
-                if self.soft_wrap {
-                    line.paint_wrapped(
-                        origin,
-                        visible_bounds,
-                        layout.line_height,
-                        wrap_boundaries,
-                        cx,
-                    );
-                } else {
-                    line.paint(origin, visible_bounds, layout.line_height, cx);
-                }
+        let element_state = self.lock();
+        let element_state = element_state
+            .as_ref()
+            .expect("measurement has not been performed");
+
+        let line_height = element_state.line_height;
+        let mut line_origin = bounds.origin;
+        let mut line_start_ix = 0;
+        for line in &element_state.lines {
+            let line_bottom = line_origin.y + line.size(line_height).height;
+            if position.y > line_bottom {
+                line_origin.y = line_bottom;
+                line_start_ix += line.len() + 1;
+            } else {
+                let position_within_line = position - line_origin;
+                let index_within_line =
+                    line.index_for_position(position_within_line, line_height)?;
+                return Some(line_start_ix + index_within_line);
             }
+        }
 
-            // Paint any custom runs that intersect this line.
-            let end_offset = offset + line.len();
-            if let Some((custom_run_ix, custom_run_range)) = custom_runs.peek().cloned() {
-                if custom_run_range.start < end_offset {
-                    let mut current_custom_run = None;
-                    if custom_run_range.start <= offset {
-                        current_custom_run = Some((custom_run_ix, custom_run_range.end, origin));
-                    }
-
-                    let mut glyph_origin = origin;
-                    let mut prev_position = 0.;
-                    let mut wrap_boundaries = wrap_boundaries.iter().copied().peekable();
-                    for (run_ix, glyph_ix, glyph) in
-                        line.runs().iter().enumerate().flat_map(|(run_ix, run)| {
-                            run.glyphs()
-                                .iter()
-                                .enumerate()
-                                .map(move |(ix, glyph)| (run_ix, ix, glyph))
-                        })
-                    {
-                        glyph_origin.set_x(glyph_origin.x() + glyph.position.x() - prev_position);
-                        prev_position = glyph.position.x();
-
-                        // If we've reached a soft wrap position, move down one line. If there
-                        // is a custom run in-progress, paint it.
-                        if wrap_boundaries
-                            .peek()
-                            .map_or(false, |b| b.run_ix == run_ix && b.glyph_ix == glyph_ix)
-                        {
-                            if let Some((run_ix, _, run_origin)) = &mut current_custom_run {
-                                let bounds = RectF::from_points(
-                                    *run_origin,
-                                    glyph_origin + vec2f(0., layout.line_height),
-                                );
-                                custom_run_callback(*run_ix, bounds, cx);
-                                *run_origin =
-                                    vec2f(origin.x(), glyph_origin.y() + layout.line_height);
-                            }
-                            wrap_boundaries.next();
-                            glyph_origin = vec2f(origin.x(), glyph_origin.y() + layout.line_height);
-                        }
+        None
+    }
+}
 
-                        // If we've reached the end of the current custom run, paint it.
-                        if let Some((run_ix, run_end_offset, run_origin)) = current_custom_run {
-                            if offset + glyph.index == run_end_offset {
-                                current_custom_run.take();
-                                let bounds = RectF::from_points(
-                                    run_origin,
-                                    glyph_origin + vec2f(0., layout.line_height),
-                                );
-                                custom_run_callback(run_ix, bounds, cx);
-                                custom_runs.next();
-                            }
-
-                            if let Some((_, run_range)) = custom_runs.peek() {
-                                if run_range.start >= end_offset {
-                                    break;
-                                }
-                                if run_range.start == offset + glyph.index {
-                                    current_custom_run =
-                                        Some((run_ix, run_range.end, glyph_origin));
-                                }
-                            }
-                        }
+pub struct InteractiveText {
+    element_id: ElementId,
+    text: StyledText,
+    click_listener:
+        Option<Box<dyn Fn(&[Range<usize>], InteractiveTextClickEvent, &mut WindowContext<'_>)>>,
+    clickable_ranges: Vec<Range<usize>>,
+}
 
-                        // If we've reached the start of a new custom run, start tracking it.
-                        if let Some((run_ix, run_range)) = custom_runs.peek() {
-                            if offset + glyph.index == run_range.start {
-                                current_custom_run = Some((*run_ix, run_range.end, glyph_origin));
-                            }
-                        }
-                    }
+struct InteractiveTextClickEvent {
+    mouse_down_index: usize,
+    mouse_up_index: usize,
+}
 
-                    // If a custom run extends beyond the end of the line, paint it.
-                    if let Some((run_ix, run_end_offset, run_origin)) = current_custom_run {
-                        let line_end = glyph_origin + vec2f(line.width() - prev_position, 0.);
-                        let bounds = RectF::from_points(
-                            run_origin,
-                            line_end + vec2f(0., layout.line_height),
-                        );
-                        custom_run_callback(run_ix, bounds, cx);
-                        if end_offset == run_end_offset {
-                            custom_runs.next();
-                        }
-                    }
-                }
-            }
+pub struct InteractiveTextState {
+    text_state: TextState,
+    mouse_down_index: Rc<Cell<Option<usize>>>,
+}
 
-            offset = end_offset + 1;
-            origin.set_y(boundaries.max_y());
+impl InteractiveText {
+    pub fn new(id: impl Into<ElementId>, text: StyledText) -> Self {
+        Self {
+            element_id: id.into(),
+            text,
+            click_listener: None,
+            clickable_ranges: Vec::new(),
         }
     }
 
-    fn rect_for_text_range(
-        &self,
-        _: Range<usize>,
-        _: RectF,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        _: &V,
-        _: &ViewContext<V>,
-    ) -> Option<RectF> {
-        None
+    pub fn on_click(
+        mut self,
+        ranges: Vec<Range<usize>>,
+        listener: impl Fn(usize, &mut WindowContext<'_>) + 'static,
+    ) -> Self {
+        self.click_listener = Some(Box::new(move |ranges, event, cx| {
+            for (range_ix, range) in ranges.iter().enumerate() {
+                if range.contains(&event.mouse_down_index) && range.contains(&event.mouse_up_index)
+                {
+                    listener(range_ix, cx);
+                }
+            }
+        }));
+        self.clickable_ranges = ranges;
+        self
     }
+}
 
-    fn debug(
-        &self,
-        bounds: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        _: &V,
-        _: &ViewContext<V>,
-    ) -> Value {
-        json!({
-            "type": "Text",
-            "bounds": bounds.to_json(),
-            "text": &self.text,
-            "style": self.style.to_json(),
-        })
+impl Element for InteractiveText {
+    type State = InteractiveTextState;
+
+    fn request_layout(
+        &mut self,
+        state: Option<Self::State>,
+        cx: &mut WindowContext,
+    ) -> (LayoutId, Self::State) {
+        if let Some(InteractiveTextState {
+            mouse_down_index, ..
+        }) = state
+        {
+            let (layout_id, text_state) = self.text.request_layout(None, cx);
+            let element_state = InteractiveTextState {
+                text_state,
+                mouse_down_index,
+            };
+            (layout_id, element_state)
+        } else {
+            let (layout_id, text_state) = self.text.request_layout(None, cx);
+            let element_state = InteractiveTextState {
+                text_state,
+                mouse_down_index: Rc::default(),
+            };
+            (layout_id, element_state)
+        }
     }
-}
 
-/// Perform text layout on a series of highlighted chunks of text.
-pub fn layout_highlighted_chunks<'a>(
-    chunks: impl Iterator<Item = (&'a str, Option<HighlightStyle>)>,
-    text_style: &TextStyle,
-    text_layout_cache: &TextLayoutCache,
-    font_cache: &Arc<FontCache>,
-    max_line_len: usize,
-    max_line_count: usize,
-) -> Vec<Line> {
-    let mut layouts = Vec::with_capacity(max_line_count);
-    let mut line = String::new();
-    let mut styles = Vec::new();
-    let mut row = 0;
-    let mut line_exceeded_max_len = false;
-    for (chunk, highlight_style) in chunks.chain([("\n", Default::default())]) {
-        for (ix, mut line_chunk) in chunk.split('\n').enumerate() {
-            if ix > 0 {
-                layouts.push(text_layout_cache.layout_str(&line, text_style.font_size, &styles));
-                line.clear();
-                styles.clear();
-                row += 1;
-                line_exceeded_max_len = false;
-                if row == max_line_count {
-                    return layouts;
+    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
+        if let Some(click_listener) = self.click_listener.take() {
+            let mouse_position = cx.mouse_position();
+            if let Some(ix) = state.text_state.index_for_position(bounds, mouse_position) {
+                if self
+                    .clickable_ranges
+                    .iter()
+                    .any(|range| range.contains(&ix))
+                    && cx.was_top_layer(&mouse_position, cx.stacking_order())
+                {
+                    cx.set_cursor_style(crate::CursorStyle::PointingHand)
                 }
             }
 
-            if !line_chunk.is_empty() && !line_exceeded_max_len {
-                let text_style = if let Some(style) = highlight_style {
-                    text_style
-                        .clone()
-                        .highlight(style, font_cache)
-                        .map(Cow::Owned)
-                        .unwrap_or_else(|_| Cow::Borrowed(text_style))
-                } else {
-                    Cow::Borrowed(text_style)
-                };
+            let text_state = state.text_state.clone();
+            let mouse_down = state.mouse_down_index.clone();
+            if let Some(mouse_down_index) = mouse_down.get() {
+                let clickable_ranges = mem::take(&mut self.clickable_ranges);
+                cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| {
+                    if phase == DispatchPhase::Bubble {
+                        if let Some(mouse_up_index) =
+                            text_state.index_for_position(bounds, event.position)
+                        {
+                            click_listener(
+                                &clickable_ranges,
+                                InteractiveTextClickEvent {
+                                    mouse_down_index,
+                                    mouse_up_index,
+                                },
+                                cx,
+                            )
+                        }
 
-                if line.len() + line_chunk.len() > max_line_len {
-                    let mut chunk_len = max_line_len - line.len();
-                    while !line_chunk.is_char_boundary(chunk_len) {
-                        chunk_len -= 1;
+                        mouse_down.take();
+                        cx.notify();
                     }
-                    line_chunk = &line_chunk[..chunk_len];
-                    line_exceeded_max_len = true;
-                }
-
-                line.push_str(line_chunk);
-                styles.push((
-                    line_chunk.len(),
-                    RunStyle {
-                        font_id: text_style.font_id,
-                        color: text_style.color,
-                        underline: text_style.underline,
-                    },
-                ));
+                });
+            } else {
+                cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
+                    if phase == DispatchPhase::Bubble {
+                        if let Some(mouse_down_index) =
+                            text_state.index_for_position(bounds, event.position)
+                        {
+                            mouse_down.set(Some(mouse_down_index));
+                            cx.notify();
+                        }
+                    }
+                });
             }
         }
-    }
 
-    layouts
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::{elements::Empty, fonts, AnyElement, AppContext, Entity, View, ViewContext};
-
-    #[crate::test(self)]
-    fn test_soft_wrapping_with_carriage_returns(cx: &mut AppContext) {
-        cx.add_window(Default::default(), |cx| {
-            let mut view = TestView;
-            fonts::with_font_cache(cx.font_cache().clone(), || {
-                let mut text = Text::new("Hello\r\n", Default::default()).with_soft_wrap(true);
-                let (_, state) = text.layout(
-                    SizeConstraint::new(Default::default(), vec2f(f32::INFINITY, f32::INFINITY)),
-                    &mut view,
-                    cx,
-                );
-                assert_eq!(state.shaped_lines.len(), 2);
-                assert_eq!(state.wrap_boundaries.len(), 2);
-            });
-            view
-        });
+        self.text.paint(bounds, &mut state.text_state, cx)
     }
+}
 
-    struct TestView;
+impl IntoElement for InteractiveText {
+    type Element = Self;
 
-    impl Entity for TestView {
-        type Event = ();
+    fn element_id(&self) -> Option<ElementId> {
+        Some(self.element_id.clone())
     }
 
-    impl View for TestView {
-        fn ui_name() -> &'static str {
-            "TestView"
-        }
-
-        fn render(&mut self, _: &mut ViewContext<Self>) -> AnyElement<Self> {
-            Empty::new().into_any()
-        }
+    fn into_element(self) -> Self::Element {
+        self
     }
 }

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

@@ -1,244 +0,0 @@
-use super::{
-    AnyElement, ContainerStyle, Element, Flex, KeystrokeLabel, MouseEventHandler, Overlay,
-    OverlayFitMode, ParentElement, Text,
-};
-use crate::{
-    fonts::TextStyle,
-    geometry::{rect::RectF, vector::Vector2F},
-    json::json,
-    Action, Axis, ElementStateHandle, SizeConstraint, Task, TypeTag, ViewContext,
-};
-use schemars::JsonSchema;
-use serde::Deserialize;
-use std::{
-    borrow::Cow,
-    cell::{Cell, RefCell},
-    ops::Range,
-    rc::Rc,
-    time::Duration,
-};
-use util::ResultExt;
-
-const DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(500);
-
-pub struct Tooltip<V> {
-    child: AnyElement<V>,
-    tooltip: Option<AnyElement<V>>,
-    _state: ElementStateHandle<Rc<TooltipState>>,
-}
-
-#[derive(Default)]
-struct TooltipState {
-    visible: Cell<bool>,
-    position: Cell<Vector2F>,
-    debounce: RefCell<Option<Task<()>>>,
-}
-
-#[derive(Clone, Deserialize, Default, JsonSchema)]
-pub struct TooltipStyle {
-    #[serde(flatten)]
-    pub container: ContainerStyle,
-    pub text: TextStyle,
-    keystroke: KeystrokeStyle,
-    pub max_text_width: Option<f32>,
-}
-
-#[derive(Clone, Deserialize, Default, JsonSchema)]
-pub struct KeystrokeStyle {
-    #[serde(flatten)]
-    container: ContainerStyle,
-    #[serde(flatten)]
-    text: TextStyle,
-}
-
-impl<V: 'static> Tooltip<V> {
-    pub fn new<Tag: 'static>(
-        id: usize,
-        text: impl Into<Cow<'static, str>>,
-        action: Option<Box<dyn Action>>,
-        style: TooltipStyle,
-        child: AnyElement<V>,
-        cx: &mut ViewContext<V>,
-    ) -> Self {
-        Self::new_dynamic(TypeTag::new::<Tag>(), id, text, action, style, child, cx)
-    }
-
-    pub fn new_dynamic(
-        mut tag: TypeTag,
-        id: usize,
-        text: impl Into<Cow<'static, str>>,
-        action: Option<Box<dyn Action>>,
-        style: TooltipStyle,
-        child: AnyElement<V>,
-        cx: &mut ViewContext<V>,
-    ) -> Self {
-        tag = tag.compose(TypeTag::new::<Self>());
-
-        let focused_view_id = cx.focused_view_id();
-
-        let state_handle = cx.default_element_state_dynamic::<Rc<TooltipState>>(tag, id);
-        let state = state_handle.read(cx).clone();
-        let text = text.into();
-
-        let tooltip = if state.visible.get() {
-            let mut collapsed_tooltip = Self::render_tooltip(
-                focused_view_id,
-                text.clone(),
-                style.clone(),
-                action.as_ref().map(|a| a.boxed_clone()),
-                true,
-            );
-            Some(
-                Overlay::new(
-                    Self::render_tooltip(focused_view_id, text, style, action, false)
-                        .constrained()
-                        .dynamically(move |constraint, view, cx| {
-                            SizeConstraint::strict_along(
-                                Axis::Vertical,
-                                collapsed_tooltip.layout(constraint, view, cx).0.y(),
-                            )
-                        }),
-                )
-                .with_fit_mode(OverlayFitMode::SwitchAnchor)
-                .with_anchor_position(state.position.get())
-                .into_any(),
-            )
-        } else {
-            None
-        };
-        let child = MouseEventHandler::new_dynamic(tag, id, cx, |_, _| child)
-            .on_hover(move |e, _, cx| {
-                let position = e.position;
-                if e.started {
-                    if !state.visible.get() {
-                        state.position.set(position);
-
-                        let mut debounce = state.debounce.borrow_mut();
-                        if debounce.is_none() {
-                            *debounce = Some(cx.spawn({
-                                let state = state.clone();
-                                |view, mut cx| async move {
-                                    cx.background().timer(DEBOUNCE_TIMEOUT).await;
-                                    state.visible.set(true);
-                                    view.update(&mut cx, |_, cx| cx.notify()).log_err();
-                                }
-                            }));
-                        }
-                    }
-                } else {
-                    state.visible.set(false);
-                    state.debounce.take();
-                    cx.notify();
-                }
-            })
-            .into_any();
-        Self {
-            child,
-            tooltip,
-            _state: state_handle,
-        }
-    }
-
-    pub fn render_tooltip(
-        focused_view_id: Option<usize>,
-        text: impl Into<Cow<'static, str>>,
-        style: TooltipStyle,
-        action: Option<Box<dyn Action>>,
-        measure: bool,
-    ) -> impl Element<V> {
-        Flex::row()
-            .with_child({
-                let text = if let Some(max_text_width) = style.max_text_width {
-                    Text::new(text, style.text)
-                        .constrained()
-                        .with_max_width(max_text_width)
-                } else {
-                    Text::new(text, style.text).constrained()
-                };
-
-                if measure {
-                    text.flex(1., false).into_any()
-                } else {
-                    text.flex(1., false).aligned().into_any()
-                }
-            })
-            .with_children(action.and_then(|action| {
-                let keystroke_label = KeystrokeLabel::new(
-                    focused_view_id?,
-                    action,
-                    style.keystroke.container,
-                    style.keystroke.text,
-                );
-                if measure {
-                    Some(keystroke_label.into_any())
-                } else {
-                    Some(keystroke_label.aligned().into_any())
-                }
-            }))
-            .contained()
-            .with_style(style.container)
-    }
-}
-
-impl<V: 'static> Element<V> for Tooltip<V> {
-    type LayoutState = ();
-    type PaintState = ();
-
-    fn layout(
-        &mut self,
-        constraint: SizeConstraint,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> (Vector2F, Self::LayoutState) {
-        let size = self.child.layout(constraint, view, cx);
-        if let Some(tooltip) = self.tooltip.as_mut() {
-            tooltip.layout(
-                SizeConstraint::new(Vector2F::zero(), cx.window_size()),
-                view,
-                cx,
-            );
-        }
-        (size, ())
-    }
-
-    fn paint(
-        &mut self,
-        bounds: RectF,
-        visible_bounds: RectF,
-        _: &mut Self::LayoutState,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) {
-        self.child.paint(bounds.origin(), visible_bounds, view, cx);
-        if let Some(tooltip) = self.tooltip.as_mut() {
-            tooltip.paint(bounds.origin(), visible_bounds, view, cx);
-        }
-    }
-
-    fn rect_for_text_range(
-        &self,
-        range: Range<usize>,
-        _: RectF,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> Option<RectF> {
-        self.child.rect_for_text_range(range, view, cx)
-    }
-
-    fn debug(
-        &self,
-        _: RectF,
-        _: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> serde_json::Value {
-        json!({
-            "child": self.child.debug(view, cx),
-            "tooltip": self.tooltip.as_ref().map(|t| t.debug(view, cx)),
-        })
-    }
-}

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

@@ -1,354 +1,316 @@
-use super::{Element, SizeConstraint};
 use crate::{
-    geometry::{
-        rect::RectF,
-        vector::{vec2f, Vector2F},
-    },
-    json::{self, json},
-    platform::ScrollWheelEvent,
-    AnyElement, MouseRegion, ViewContext,
+    point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Element,
+    ElementId, InteractiveElement, InteractiveElementState, Interactivity, IntoElement, LayoutId,
+    Pixels, Point, Render, Size, StyleRefinement, Styled, View, ViewContext, WindowContext,
 };
-use json::ToJson;
+use smallvec::SmallVec;
 use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
-
-#[derive(Clone, Default)]
-pub struct UniformListState(Rc<RefCell<StateInner>>);
-
-#[derive(Debug)]
-pub enum ScrollTarget {
-    Show(usize),
-    Center(usize),
-}
-
-impl UniformListState {
-    pub fn scroll_to(&self, scroll_to: ScrollTarget) {
-        self.0.borrow_mut().scroll_to = Some(scroll_to);
-    }
-
-    pub fn scroll_top(&self) -> f32 {
-        self.0.borrow().scroll_top
+use taffy::style::Overflow;
+
+/// uniform_list provides lazy rendering for a set of items that are of uniform height.
+/// When rendered into a container with overflow-y: hidden and a fixed (or max) height,
+/// uniform_list will only render the visible subset of items.
+#[track_caller]
+pub fn uniform_list<I, R, V>(
+    view: View<V>,
+    id: I,
+    item_count: usize,
+    f: impl 'static + Fn(&mut V, Range<usize>, &mut ViewContext<V>) -> Vec<R>,
+) -> UniformList
+where
+    I: Into<ElementId>,
+    R: IntoElement,
+    V: Render,
+{
+    let id = id.into();
+    let mut base_style = StyleRefinement::default();
+    base_style.overflow.y = Some(Overflow::Scroll);
+
+    let render_range = move |range, cx: &mut WindowContext| {
+        view.update(cx, |this, cx| {
+            f(this, range, cx)
+                .into_iter()
+                .map(|component| component.into_any_element())
+                .collect()
+        })
+    };
+
+    UniformList {
+        id: id.clone(),
+        item_count,
+        item_to_measure_index: 0,
+        render_items: Box::new(render_range),
+        interactivity: Interactivity {
+            element_id: Some(id),
+            base_style: Box::new(base_style),
+
+            #[cfg(debug_assertions)]
+            location: Some(*core::panic::Location::caller()),
+
+            ..Default::default()
+        },
+        scroll_handle: None,
     }
 }
 
-#[derive(Default)]
-struct StateInner {
-    scroll_top: f32,
-    scroll_to: Option<ScrollTarget>,
-}
-
-pub struct UniformListLayoutState<V> {
-    scroll_max: f32,
-    item_height: f32,
-    items: Vec<AnyElement<V>>,
-}
-
-pub struct UniformList<V> {
-    state: UniformListState,
+pub struct UniformList {
+    id: ElementId,
     item_count: usize,
-    #[allow(clippy::type_complexity)]
-    append_items: Box<dyn Fn(&mut V, Range<usize>, &mut Vec<AnyElement<V>>, &mut ViewContext<V>)>,
-    padding_top: f32,
-    padding_bottom: f32,
-    get_width_from_item: Option<usize>,
-    view_id: usize,
+    item_to_measure_index: usize,
+    render_items:
+        Box<dyn for<'a> Fn(Range<usize>, &'a mut WindowContext) -> SmallVec<[AnyElement; 64]>>,
+    interactivity: Interactivity,
+    scroll_handle: Option<UniformListScrollHandle>,
 }
 
-impl<V: 'static> UniformList<V> {
-    pub fn new<F>(
-        state: UniformListState,
-        item_count: usize,
-        cx: &mut ViewContext<V>,
-        append_items: F,
-    ) -> Self
-    where
-        F: 'static + Fn(&mut V, Range<usize>, &mut Vec<AnyElement<V>>, &mut ViewContext<V>),
-    {
-        Self {
-            state,
-            item_count,
-            append_items: Box::new(append_items),
-            padding_top: 0.,
-            padding_bottom: 0.,
-            get_width_from_item: None,
-            view_id: cx.handle().id(),
-        }
-    }
-
-    pub fn with_width_from_item(mut self, item_ix: Option<usize>) -> Self {
-        self.get_width_from_item = item_ix;
-        self
-    }
-
-    pub fn with_padding_top(mut self, padding: f32) -> Self {
-        self.padding_top = padding;
-        self
-    }
-
-    pub fn with_padding_bottom(mut self, padding: f32) -> Self {
-        self.padding_bottom = padding;
-        self
-    }
-
-    fn scroll(
-        state: UniformListState,
-        _: Vector2F,
-        mut delta: Vector2F,
-        precise: bool,
-        scroll_max: f32,
-        cx: &mut ViewContext<V>,
-    ) -> bool {
-        if !precise {
-            delta *= 20.;
-        }
+#[derive(Clone, Default)]
+pub struct UniformListScrollHandle(Rc<RefCell<Option<ScrollHandleState>>>);
 
-        let mut state = state.0.borrow_mut();
-        state.scroll_top = (state.scroll_top - delta.y()).max(0.0).min(scroll_max);
-        cx.notify();
+#[derive(Clone, Debug)]
+struct ScrollHandleState {
+    item_height: Pixels,
+    list_height: Pixels,
+    scroll_offset: Rc<RefCell<Point<Pixels>>>,
+}
 
-        true
+impl UniformListScrollHandle {
+    pub fn new() -> Self {
+        Self(Rc::new(RefCell::new(None)))
     }
 
-    fn autoscroll(&mut self, scroll_max: f32, list_height: f32, item_height: f32) {
-        let mut state = self.state.0.borrow_mut();
-
-        if let Some(scroll_to) = state.scroll_to.take() {
-            let item_ix;
-            let center;
-            match scroll_to {
-                ScrollTarget::Show(ix) => {
-                    item_ix = ix;
-                    center = false;
-                }
-                ScrollTarget::Center(ix) => {
-                    item_ix = ix;
-                    center = true;
-                }
-            }
-
-            let item_top = self.padding_top + item_ix as f32 * item_height;
-            let item_bottom = item_top + item_height;
-            if center {
-                let item_center = item_top + item_height / 2.;
-                state.scroll_top = (item_center - list_height / 2.).max(0.);
-            } else {
-                let scroll_bottom = state.scroll_top + list_height;
-                if item_top < state.scroll_top {
-                    state.scroll_top = item_top;
-                } else if item_bottom > scroll_bottom {
-                    state.scroll_top = item_bottom - list_height;
-                }
+    pub fn scroll_to_item(&self, ix: usize) {
+        if let Some(state) = &*self.0.borrow() {
+            let mut scroll_offset = state.scroll_offset.borrow_mut();
+            let item_top = state.item_height * ix;
+            let item_bottom = item_top + state.item_height;
+            let scroll_top = -scroll_offset.y;
+            if item_top < scroll_top {
+                scroll_offset.y = -item_top;
+            } else if item_bottom > scroll_top + state.list_height {
+                scroll_offset.y = -(item_bottom - state.list_height);
             }
         }
+    }
 
-        if state.scroll_top > scroll_max {
-            state.scroll_top = scroll_max;
+    pub fn scroll_top(&self) -> Pixels {
+        if let Some(state) = &*self.0.borrow() {
+            -state.scroll_offset.borrow().y
+        } else {
+            Pixels::ZERO
         }
     }
+}
 
-    fn scroll_top(&self) -> f32 {
-        self.state.0.borrow().scroll_top
+impl Styled for UniformList {
+    fn style(&mut self) -> &mut StyleRefinement {
+        &mut self.interactivity.base_style
     }
 }
 
-impl<V: 'static> Element<V> for UniformList<V> {
-    type LayoutState = UniformListLayoutState<V>;
-    type PaintState = ();
+#[derive(Default)]
+pub struct UniformListState {
+    interactive: InteractiveElementState,
+    item_size: Size<Pixels>,
+}
 
-    fn layout(
-        &mut self,
-        constraint: SizeConstraint,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> (Vector2F, Self::LayoutState) {
-        if constraint.max.y().is_infinite() {
-            unimplemented!(
-                "UniformList does not support being rendered with an unconstrained height"
-            );
-        }
+impl Element for UniformList {
+    type State = UniformListState;
 
-        let no_items = (
-            constraint.min,
-            UniformListLayoutState {
-                item_height: 0.,
-                scroll_max: 0.,
-                items: Default::default(),
-            },
-        );
+    fn request_layout(
+        &mut self,
+        state: Option<Self::State>,
+        cx: &mut WindowContext,
+    ) -> (LayoutId, Self::State) {
+        let max_items = self.item_count;
+        let item_size = state
+            .as_ref()
+            .map(|s| s.item_size)
+            .unwrap_or_else(|| self.measure_item(None, cx));
+
+        let (layout_id, interactive) =
+            self.interactivity
+                .layout(state.map(|s| s.interactive), cx, |style, cx| {
+                    cx.request_measured_layout(
+                        style,
+                        move |known_dimensions, available_space, _cx| {
+                            let desired_height = item_size.height * max_items;
+                            let width =
+                                known_dimensions
+                                    .width
+                                    .unwrap_or(match available_space.width {
+                                        AvailableSpace::Definite(x) => x,
+                                        AvailableSpace::MinContent | AvailableSpace::MaxContent => {
+                                            item_size.width
+                                        }
+                                    });
+
+                            let height = match available_space.height {
+                                AvailableSpace::Definite(height) => desired_height.min(height),
+                                AvailableSpace::MinContent | AvailableSpace::MaxContent => {
+                                    desired_height
+                                }
+                            };
+                            size(width, height)
+                        },
+                    )
+                });
+
+        let element_state = UniformListState {
+            interactive,
+            item_size,
+        };
 
-        if self.item_count == 0 {
-            return no_items;
-        }
+        (layout_id, element_state)
+    }
 
-        let mut items = Vec::new();
-        let mut size = constraint.max;
-        let mut item_size;
-        let sample_item_ix;
-        let sample_item;
-        if let Some(sample_ix) = self.get_width_from_item {
-            (self.append_items)(view, sample_ix..sample_ix + 1, &mut items, cx);
-            sample_item_ix = sample_ix;
-
-            if let Some(mut item) = items.pop() {
-                item_size = item.layout(constraint, view, cx);
-                size.set_x(item_size.x());
-                sample_item = item;
-            } else {
-                return no_items;
-            }
-        } else {
-            (self.append_items)(view, 0..1, &mut items, cx);
-            sample_item_ix = 0;
-            if let Some(mut item) = items.pop() {
-                item_size = item.layout(
-                    SizeConstraint::new(
-                        vec2f(constraint.max.x(), 0.0),
-                        vec2f(constraint.max.x(), f32::INFINITY),
-                    ),
-                    view,
-                    cx,
-                );
-                item_size.set_x(size.x());
-                sample_item = item
-            } else {
-                return no_items;
-            }
-        }
+    fn paint(
+        &mut self,
+        bounds: Bounds<crate::Pixels>,
+        element_state: &mut Self::State,
+        cx: &mut WindowContext,
+    ) {
+        let style =
+            self.interactivity
+                .compute_style(Some(bounds), &mut element_state.interactive, cx);
+        let border = style.border_widths.to_pixels(cx.rem_size());
+        let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
+
+        let padded_bounds = Bounds::from_corners(
+            bounds.origin + point(border.left + padding.left, border.top + padding.top),
+            bounds.lower_right()
+                - point(border.right + padding.right, border.bottom + padding.bottom),
+        );
 
-        let item_constraint = SizeConstraint {
-            min: item_size,
-            max: vec2f(constraint.max.x(), item_size.y()),
+        let item_size = element_state.item_size;
+        let content_size = Size {
+            width: padded_bounds.size.width,
+            height: item_size.height * self.item_count + padding.top + padding.bottom,
         };
-        let item_height = item_size.y();
 
-        let scroll_height = self.item_count as f32 * item_height;
-        if scroll_height < size.y() {
-            size.set_y(size.y().min(scroll_height).max(constraint.min.y()));
-        }
+        let shared_scroll_offset = element_state
+            .interactive
+            .scroll_offset
+            .get_or_insert_with(|| {
+                if let Some(scroll_handle) = self.scroll_handle.as_ref() {
+                    if let Some(scroll_handle) = scroll_handle.0.borrow().as_ref() {
+                        return scroll_handle.scroll_offset.clone();
+                    }
+                }
 
-        let scroll_height =
-            item_height * self.item_count as f32 + self.padding_top + self.padding_bottom;
-        let scroll_max = (scroll_height - size.y()).max(0.);
-        self.autoscroll(scroll_max, size.y(), item_height);
+                Rc::default()
+            })
+            .clone();
 
-        let start = cmp::min(
-            ((self.scroll_top() - self.padding_top) / item_height.max(1.)) as usize,
-            self.item_count,
-        );
-        let end = cmp::min(
-            self.item_count,
-            start + (size.y() / item_height.max(1.)).ceil() as usize + 1,
-        );
+        let item_height = self.measure_item(Some(padded_bounds.size.width), cx).height;
 
-        if (start..end).contains(&sample_item_ix) {
-            if sample_item_ix > start {
-                (self.append_items)(view, start..sample_item_ix, &mut items, cx);
-            }
+        self.interactivity.paint(
+            bounds,
+            content_size,
+            &mut element_state.interactive,
+            cx,
+            |style, mut scroll_offset, cx| {
+                let border = style.border_widths.to_pixels(cx.rem_size());
+                let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
 
-            items.push(sample_item);
+                let padded_bounds = Bounds::from_corners(
+                    bounds.origin + point(border.left + padding.left, border.top),
+                    bounds.lower_right() - point(border.right + padding.right, border.bottom),
+                );
 
-            if sample_item_ix < end {
-                (self.append_items)(view, sample_item_ix + 1..end, &mut items, cx);
-            }
-        } else {
-            (self.append_items)(view, start..end, &mut items, cx);
-        }
+                if self.item_count > 0 {
+                    let content_height =
+                        item_height * self.item_count + padding.top + padding.bottom;
+                    let min_scroll_offset = padded_bounds.size.height - content_height;
+                    let is_scrolled = scroll_offset.y != px(0.);
 
-        for item in &mut items {
-            let item_size = item.layout(item_constraint, view, cx);
-            if item_size.x() > size.x() {
-                size.set_x(item_size.x());
-            }
-        }
+                    if is_scrolled && scroll_offset.y < min_scroll_offset {
+                        shared_scroll_offset.borrow_mut().y = min_scroll_offset;
+                        scroll_offset.y = min_scroll_offset;
+                    }
+
+                    if let Some(scroll_handle) = self.scroll_handle.clone() {
+                        scroll_handle.0.borrow_mut().replace(ScrollHandleState {
+                            item_height,
+                            list_height: padded_bounds.size.height,
+                            scroll_offset: shared_scroll_offset,
+                        });
+                    }
 
-        (
-            size,
-            UniformListLayoutState {
-                item_height,
-                scroll_max,
-                items,
+                    let first_visible_element_ix =
+                        (-(scroll_offset.y + padding.top) / item_height).floor() as usize;
+                    let last_visible_element_ix = ((-scroll_offset.y + padded_bounds.size.height)
+                        / item_height)
+                        .ceil() as usize;
+                    let visible_range = first_visible_element_ix
+                        ..cmp::min(last_visible_element_ix, self.item_count);
+
+                    let mut items = (self.render_items)(visible_range.clone(), cx);
+                    cx.with_z_index(1, |cx| {
+                        let content_mask = ContentMask { bounds };
+                        cx.with_content_mask(Some(content_mask), |cx| {
+                            for (item, ix) in items.iter_mut().zip(visible_range) {
+                                let item_origin = padded_bounds.origin
+                                    + point(
+                                        px(0.),
+                                        item_height * ix + scroll_offset.y + padding.top,
+                                    );
+                                let available_space = size(
+                                    AvailableSpace::Definite(padded_bounds.size.width),
+                                    AvailableSpace::Definite(item_height),
+                                );
+                                item.draw(item_origin, available_space, cx);
+                            }
+                        });
+                    });
+                }
             },
         )
     }
+}
 
-    fn paint(
-        &mut self,
-        bounds: RectF,
-        visible_bounds: RectF,
-        layout: &mut Self::LayoutState,
-        view: &mut V,
-        cx: &mut ViewContext<V>,
-    ) -> Self::PaintState {
-        let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();
-
-        cx.scene().push_layer(Some(visible_bounds));
-
-        cx.scene().push_mouse_region(
-            MouseRegion::new::<Self>(self.view_id, 0, visible_bounds).on_scroll({
-                let scroll_max = layout.scroll_max;
-                let state = self.state.clone();
-                move |event, _, cx| {
-                    let ScrollWheelEvent {
-                        position, delta, ..
-                    } = event.platform_event;
-                    if !Self::scroll(
-                        state.clone(),
-                        position,
-                        *delta.raw(),
-                        delta.precise(),
-                        scroll_max,
-                        cx,
-                    ) {
-                        cx.propagate_event();
-                    }
-                }
-            }),
-        );
+impl IntoElement for UniformList {
+    type Element = Self;
 
-        let mut item_origin = bounds.origin()
-            - vec2f(
-                0.,
-                (self.state.scroll_top() - self.padding_top) % layout.item_height,
-            );
+    fn element_id(&self) -> Option<crate::ElementId> {
+        Some(self.id.clone())
+    }
 
-        for item in &mut layout.items {
-            item.paint(item_origin, visible_bounds, view, cx);
-            item_origin += vec2f(0.0, layout.item_height);
-        }
+    fn into_element(self) -> Self::Element {
+        self
+    }
+}
 
-        cx.scene().pop_layer();
+impl UniformList {
+    pub fn with_width_from_item(mut self, item_index: Option<usize>) -> Self {
+        self.item_to_measure_index = item_index.unwrap_or(0);
+        self
     }
 
-    fn rect_for_text_range(
-        &self,
-        range: Range<usize>,
-        _: RectF,
-        _: RectF,
-        layout: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> Option<RectF> {
-        layout
-            .items
-            .iter()
-            .find_map(|child| child.rect_for_text_range(range.clone(), view, cx))
+    fn measure_item(&self, list_width: Option<Pixels>, cx: &mut WindowContext) -> Size<Pixels> {
+        if self.item_count == 0 {
+            return Size::default();
+        }
+
+        let item_ix = cmp::min(self.item_to_measure_index, self.item_count - 1);
+        let mut items = (self.render_items)(item_ix..item_ix + 1, cx);
+        let mut item_to_measure = items.pop().unwrap();
+        let available_space = size(
+            list_width.map_or(AvailableSpace::MinContent, |width| {
+                AvailableSpace::Definite(width)
+            }),
+            AvailableSpace::MinContent,
+        );
+        item_to_measure.measure(available_space, cx)
     }
 
-    fn debug(
-        &self,
-        bounds: RectF,
-        layout: &Self::LayoutState,
-        _: &Self::PaintState,
-        view: &V,
-        cx: &ViewContext<V>,
-    ) -> json::Value {
-        json!({
-            "type": "UniformList",
-            "bounds": bounds.to_json(),
-            "scroll_max": layout.scroll_max,
-            "item_height": layout.item_height,
-            "items": layout.items.iter().map(|item| item.debug(view, cx)).collect::<Vec<json::Value>>()
+    pub fn track_scroll(mut self, handle: UniformListScrollHandle) -> Self {
+        self.scroll_handle = Some(handle);
+        self
+    }
+}
 
-        })
+impl InteractiveElement for UniformList {
+    fn interactivity(&mut self) -> &mut crate::Interactivity {
+        &mut self.interactivity
     }
 }

crates/gpui/src/executor.rs 🔗

@@ -1,916 +1,372 @@
-use anyhow::{anyhow, Result};
-use async_task::Runnable;
-use futures::channel::mpsc;
-use smol::{channel, prelude::*, Executor};
+use crate::{AppContext, PlatformDispatcher};
+use futures::{channel::mpsc, pin_mut, FutureExt};
+use smol::prelude::*;
 use std::{
-    any::Any,
-    fmt::{self, Display},
+    fmt::Debug,
     marker::PhantomData,
     mem,
-    panic::Location,
+    num::NonZeroUsize,
     pin::Pin,
     rc::Rc,
-    sync::Arc,
+    sync::{
+        atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst},
+        Arc,
+    },
     task::{Context, Poll},
-    thread,
     time::Duration,
 };
+use util::TryFutureExt;
+use waker_fn::waker_fn;
 
-use crate::{
-    platform::{self, Dispatcher},
-    util, AppContext,
-};
+#[cfg(any(test, feature = "test-support"))]
+use rand::rngs::StdRng;
 
-pub enum Foreground {
-    Platform {
-        dispatcher: Arc<dyn platform::Dispatcher>,
-        _not_send_or_sync: PhantomData<Rc<()>>,
-    },
-    #[cfg(any(test, feature = "test-support"))]
-    Deterministic {
-        cx_id: usize,
-        executor: Arc<Deterministic>,
-    },
+#[derive(Clone)]
+pub struct BackgroundExecutor {
+    dispatcher: Arc<dyn PlatformDispatcher>,
 }
 
-pub enum Background {
-    #[cfg(any(test, feature = "test-support"))]
-    Deterministic { executor: Arc<Deterministic> },
-    Production {
-        executor: Arc<smol::Executor<'static>>,
-        _stop: channel::Sender<()>,
-    },
+#[derive(Clone)]
+pub struct ForegroundExecutor {
+    dispatcher: Arc<dyn PlatformDispatcher>,
+    not_send: PhantomData<Rc<()>>,
 }
 
-type AnyLocalFuture = Pin<Box<dyn 'static + Future<Output = Box<dyn Any + 'static>>>>;
-type AnyFuture = Pin<Box<dyn 'static + Send + Future<Output = Box<dyn Any + Send + 'static>>>>;
-type AnyTask = async_task::Task<Box<dyn Any + Send + 'static>>;
-type AnyLocalTask = async_task::Task<Box<dyn Any + 'static>>;
-
 #[must_use]
+#[derive(Debug)]
 pub enum Task<T> {
     Ready(Option<T>),
-    Local {
-        any_task: AnyLocalTask,
-        result_type: PhantomData<T>,
-    },
-    Send {
-        any_task: AnyTask,
-        result_type: PhantomData<T>,
-    },
+    Spawned(async_task::Task<T>),
 }
 
-unsafe impl<T: Send> Send for Task<T> {}
-
-#[cfg(any(test, feature = "test-support"))]
-struct DeterministicState {
-    rng: rand::prelude::StdRng,
-    seed: u64,
-    scheduled_from_foreground: collections::HashMap<usize, Vec<ForegroundRunnable>>,
-    scheduled_from_background: Vec<BackgroundRunnable>,
-    forbid_parking: bool,
-    block_on_ticks: std::ops::RangeInclusive<usize>,
-    now: std::time::Instant,
-    next_timer_id: usize,
-    pending_timers: Vec<(usize, std::time::Instant, postage::barrier::Sender)>,
-    waiting_backtrace: Option<backtrace::Backtrace>,
-    next_runnable_id: usize,
-    poll_history: Vec<ExecutorEvent>,
-    previous_poll_history: Option<Vec<ExecutorEvent>>,
-    enable_runnable_backtraces: bool,
-    runnable_backtraces: collections::HashMap<usize, backtrace::Backtrace>,
-}
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum ExecutorEvent {
-    PollRunnable { id: usize },
-    EnqueueRunnable { id: usize },
-}
-
-#[cfg(any(test, feature = "test-support"))]
-struct ForegroundRunnable {
-    id: usize,
-    runnable: Runnable,
-    main: bool,
-}
+impl<T> Task<T> {
+    pub fn ready(val: T) -> Self {
+        Task::Ready(Some(val))
+    }
 
-#[cfg(any(test, feature = "test-support"))]
-struct BackgroundRunnable {
-    id: usize,
-    runnable: Runnable,
+    pub fn detach(self) {
+        match self {
+            Task::Ready(_) => {}
+            Task::Spawned(task) => task.detach(),
+        }
+    }
 }
 
-#[cfg(any(test, feature = "test-support"))]
-pub struct Deterministic {
-    state: Arc<parking_lot::Mutex<DeterministicState>>,
-    parker: parking_lot::Mutex<parking::Parker>,
+impl<E, T> Task<Result<T, E>>
+where
+    T: 'static,
+    E: 'static + Debug,
+{
+    #[track_caller]
+    pub fn detach_and_log_err(self, cx: &mut AppContext) {
+        let location = core::panic::Location::caller();
+        cx.foreground_executor()
+            .spawn(self.log_tracked_err(*location))
+            .detach();
+    }
 }
 
-#[must_use]
-pub enum Timer {
-    Production(smol::Timer),
-    #[cfg(any(test, feature = "test-support"))]
-    Deterministic(DeterministicTimer),
-}
+impl<T> Future for Task<T> {
+    type Output = T;
 
-#[cfg(any(test, feature = "test-support"))]
-pub struct DeterministicTimer {
-    rx: postage::barrier::Receiver,
-    id: usize,
-    state: Arc<parking_lot::Mutex<DeterministicState>>,
+    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
+        match unsafe { self.get_unchecked_mut() } {
+            Task::Ready(val) => Poll::Ready(val.take().unwrap()),
+            Task::Spawned(task) => task.poll(cx),
+        }
+    }
 }
 
-#[cfg(any(test, feature = "test-support"))]
-impl Deterministic {
-    pub fn new(seed: u64) -> Arc<Self> {
-        use rand::prelude::*;
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
+pub struct TaskLabel(NonZeroUsize);
 
-        Arc::new(Self {
-            state: Arc::new(parking_lot::Mutex::new(DeterministicState {
-                rng: StdRng::seed_from_u64(seed),
-                seed,
-                scheduled_from_foreground: Default::default(),
-                scheduled_from_background: Default::default(),
-                forbid_parking: false,
-                block_on_ticks: 0..=1000,
-                now: std::time::Instant::now(),
-                next_timer_id: Default::default(),
-                pending_timers: Default::default(),
-                waiting_backtrace: None,
-                next_runnable_id: 0,
-                poll_history: Default::default(),
-                previous_poll_history: Default::default(),
-                enable_runnable_backtraces: false,
-                runnable_backtraces: Default::default(),
-            })),
-            parker: Default::default(),
-        })
+impl Default for TaskLabel {
+    fn default() -> Self {
+        Self::new()
     }
+}
 
-    pub fn execution_history(&self) -> Vec<ExecutorEvent> {
-        self.state.lock().poll_history.clone()
+impl TaskLabel {
+    pub fn new() -> Self {
+        static NEXT_TASK_LABEL: AtomicUsize = AtomicUsize::new(1);
+        Self(NEXT_TASK_LABEL.fetch_add(1, SeqCst).try_into().unwrap())
     }
+}
 
-    pub fn set_previous_execution_history(&self, history: Option<Vec<ExecutorEvent>>) {
-        self.state.lock().previous_poll_history = history;
-    }
+type AnyLocalFuture<R> = Pin<Box<dyn 'static + Future<Output = R>>>;
 
-    pub fn enable_runnable_backtrace(&self) {
-        self.state.lock().enable_runnable_backtraces = true;
-    }
+type AnyFuture<R> = Pin<Box<dyn 'static + Send + Future<Output = R>>>;
 
-    pub fn runnable_backtrace(&self, runnable_id: usize) -> backtrace::Backtrace {
-        let mut backtrace = self.state.lock().runnable_backtraces[&runnable_id].clone();
-        backtrace.resolve();
-        backtrace
+impl BackgroundExecutor {
+    pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
+        Self { dispatcher }
     }
 
-    pub fn build_background(self: &Arc<Self>) -> Arc<Background> {
-        Arc::new(Background::Deterministic {
-            executor: self.clone(),
-        })
+    /// Enqueues the given future to be run to completion on a background thread.
+    pub fn spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
+    where
+        R: Send + 'static,
+    {
+        self.spawn_internal::<R>(Box::pin(future), None)
     }
 
-    pub fn build_foreground(self: &Arc<Self>, id: usize) -> Rc<Foreground> {
-        Rc::new(Foreground::Deterministic {
-            cx_id: id,
-            executor: self.clone(),
-        })
+    /// Enqueues the given future to be run to completion on a background thread.
+    /// The given label can be used to control the priority of the task in tests.
+    pub fn spawn_labeled<R>(
+        &self,
+        label: TaskLabel,
+        future: impl Future<Output = R> + Send + 'static,
+    ) -> Task<R>
+    where
+        R: Send + 'static,
+    {
+        self.spawn_internal::<R>(Box::pin(future), Some(label))
     }
 
-    fn spawn_from_foreground(
+    fn spawn_internal<R: Send + 'static>(
         &self,
-        cx_id: usize,
-        future: AnyLocalFuture,
-        main: bool,
-    ) -> AnyLocalTask {
-        let state = self.state.clone();
-        let id;
-        {
-            let mut state = state.lock();
-            id = util::post_inc(&mut state.next_runnable_id);
-            if state.enable_runnable_backtraces {
-                state
-                    .runnable_backtraces
-                    .insert(id, backtrace::Backtrace::new_unresolved());
-            }
-        }
-
-        let unparker = self.parker.lock().unparker();
-        let (runnable, task) = async_task::spawn_local(future, move |runnable| {
-            let mut state = state.lock();
-            state.push_to_history(ExecutorEvent::EnqueueRunnable { id });
-            state
-                .scheduled_from_foreground
-                .entry(cx_id)
-                .or_default()
-                .push(ForegroundRunnable { id, runnable, main });
-            unparker.unpark();
-        });
+        future: AnyFuture<R>,
+        label: Option<TaskLabel>,
+    ) -> Task<R> {
+        let dispatcher = self.dispatcher.clone();
+        let (runnable, task) =
+            async_task::spawn(future, move |runnable| dispatcher.dispatch(runnable, label));
         runnable.schedule();
-        task
+        Task::Spawned(task)
     }
 
-    fn spawn(&self, future: AnyFuture) -> AnyTask {
-        let state = self.state.clone();
-        let id;
-        {
-            let mut state = state.lock();
-            id = util::post_inc(&mut state.next_runnable_id);
-            if state.enable_runnable_backtraces {
-                state
-                    .runnable_backtraces
-                    .insert(id, backtrace::Backtrace::new_unresolved());
-            }
+    #[cfg(any(test, feature = "test-support"))]
+    #[track_caller]
+    pub fn block_test<R>(&self, future: impl Future<Output = R>) -> R {
+        if let Ok(value) = self.block_internal(false, future, usize::MAX) {
+            value
+        } else {
+            unreachable!()
         }
-
-        let unparker = self.parker.lock().unparker();
-        let (runnable, task) = async_task::spawn(future, move |runnable| {
-            let mut state = state.lock();
-            state
-                .poll_history
-                .push(ExecutorEvent::EnqueueRunnable { id });
-            state
-                .scheduled_from_background
-                .push(BackgroundRunnable { id, runnable });
-            unparker.unpark();
-        });
-        runnable.schedule();
-        task
     }
 
-    fn run<'a>(
-        &self,
-        cx_id: usize,
-        main_future: Pin<Box<dyn 'a + Future<Output = Box<dyn Any>>>>,
-    ) -> Box<dyn Any> {
-        use std::sync::atomic::{AtomicBool, Ordering::SeqCst};
-
-        let woken = Arc::new(AtomicBool::new(false));
-
-        let state = self.state.clone();
-        let id;
-        {
-            let mut state = state.lock();
-            id = util::post_inc(&mut state.next_runnable_id);
-            if state.enable_runnable_backtraces {
-                state
-                    .runnable_backtraces
-                    .insert(id, backtrace::Backtrace::new_unresolved());
-            }
-        }
-
-        let unparker = self.parker.lock().unparker();
-        let (runnable, mut main_task) = unsafe {
-            async_task::spawn_unchecked(main_future, move |runnable| {
-                let state = &mut *state.lock();
-                state
-                    .scheduled_from_foreground
-                    .entry(cx_id)
-                    .or_default()
-                    .push(ForegroundRunnable {
-                        id: util::post_inc(&mut state.next_runnable_id),
-                        runnable,
-                        main: true,
-                    });
-                unparker.unpark();
-            })
-        };
-        runnable.schedule();
-
-        loop {
-            if let Some(result) = self.run_internal(woken.clone(), Some(&mut main_task)) {
-                return result;
-            }
-
-            if !woken.load(SeqCst) {
-                self.state.lock().will_park();
-            }
-
-            woken.store(false, SeqCst);
-            self.parker.lock().park();
+    pub fn block<R>(&self, future: impl Future<Output = R>) -> R {
+        if let Ok(value) = self.block_internal(true, future, usize::MAX) {
+            value
+        } else {
+            unreachable!()
         }
     }
 
-    pub fn run_until_parked(&self) {
-        use std::sync::atomic::AtomicBool;
-        let woken = Arc::new(AtomicBool::new(false));
-        self.run_internal(woken, None);
-    }
-
-    fn run_internal(
+    #[track_caller]
+    pub(crate) fn block_internal<R>(
         &self,
-        woken: Arc<std::sync::atomic::AtomicBool>,
-        mut main_task: Option<&mut AnyLocalTask>,
-    ) -> Option<Box<dyn Any>> {
-        use rand::prelude::*;
-        use std::sync::atomic::Ordering::SeqCst;
-
-        let unparker = self.parker.lock().unparker();
-        let waker = waker_fn::waker_fn(move || {
-            woken.store(true, SeqCst);
-            unparker.unpark();
+        background_only: bool,
+        future: impl Future<Output = R>,
+        mut max_ticks: usize,
+    ) -> Result<R, ()> {
+        pin_mut!(future);
+        let unparker = self.dispatcher.unparker();
+        let awoken = Arc::new(AtomicBool::new(false));
+
+        let waker = waker_fn({
+            let awoken = awoken.clone();
+            move || {
+                awoken.store(true, SeqCst);
+                unparker.unpark();
+            }
         });
+        let mut cx = std::task::Context::from_waker(&waker);
 
-        let mut cx = Context::from_waker(&waker);
         loop {
-            let mut state = self.state.lock();
-
-            if state.scheduled_from_foreground.is_empty()
-                && state.scheduled_from_background.is_empty()
-            {
-                if let Some(main_task) = main_task {
-                    if let Poll::Ready(result) = main_task.poll(&mut cx) {
-                        return Some(result);
+            match future.as_mut().poll(&mut cx) {
+                Poll::Ready(result) => return Ok(result),
+                Poll::Pending => {
+                    if max_ticks == 0 {
+                        return Err(());
                     }
-                }
+                    max_ticks -= 1;
 
-                return None;
-            }
-
-            if !state.scheduled_from_background.is_empty() && state.rng.gen() {
-                let background_len = state.scheduled_from_background.len();
-                let ix = state.rng.gen_range(0..background_len);
-                let background_runnable = state.scheduled_from_background.remove(ix);
-                state.push_to_history(ExecutorEvent::PollRunnable {
-                    id: background_runnable.id,
-                });
-                drop(state);
-                background_runnable.runnable.run();
-            } else if !state.scheduled_from_foreground.is_empty() {
-                let available_cx_ids = state
-                    .scheduled_from_foreground
-                    .keys()
-                    .copied()
-                    .collect::<Vec<_>>();
-                let cx_id_to_run = *available_cx_ids.iter().choose(&mut state.rng).unwrap();
-                let scheduled_from_cx = state
-                    .scheduled_from_foreground
-                    .get_mut(&cx_id_to_run)
-                    .unwrap();
-                let foreground_runnable = scheduled_from_cx.remove(0);
-                if scheduled_from_cx.is_empty() {
-                    state.scheduled_from_foreground.remove(&cx_id_to_run);
-                }
-                state.push_to_history(ExecutorEvent::PollRunnable {
-                    id: foreground_runnable.id,
-                });
-
-                drop(state);
-
-                foreground_runnable.runnable.run();
-                if let Some(main_task) = main_task.as_mut() {
-                    if foreground_runnable.main {
-                        if let Poll::Ready(result) = main_task.poll(&mut cx) {
-                            return Some(result);
+                    if !self.dispatcher.tick(background_only) {
+                        if awoken.swap(false, SeqCst) {
+                            continue;
                         }
-                    }
-                }
-            }
-        }
-    }
 
-    fn block<F, T>(&self, future: &mut F, max_ticks: usize) -> Option<T>
-    where
-        F: Unpin + Future<Output = T>,
-    {
-        use rand::prelude::*;
-
-        let unparker = self.parker.lock().unparker();
-        let waker = waker_fn::waker_fn(move || {
-            unparker.unpark();
-        });
-
-        let mut cx = Context::from_waker(&waker);
-        for _ in 0..max_ticks {
-            let mut state = self.state.lock();
-            let runnable_count = state.scheduled_from_background.len();
-            let ix = state.rng.gen_range(0..=runnable_count);
-            if ix < state.scheduled_from_background.len() {
-                let background_runnable = state.scheduled_from_background.remove(ix);
-                state.push_to_history(ExecutorEvent::PollRunnable {
-                    id: background_runnable.id,
-                });
-                drop(state);
-                background_runnable.runnable.run();
-            } else {
-                drop(state);
-                if let Poll::Ready(result) = future.poll(&mut cx) {
-                    return Some(result);
-                }
-                let mut state = self.state.lock();
-                if state.scheduled_from_background.is_empty() {
-                    state.will_park();
-                    drop(state);
-                    self.parker.lock().park();
-                }
-
-                continue;
-            }
-        }
-
-        None
-    }
-
-    pub fn timer(&self, duration: Duration) -> Timer {
-        let (tx, rx) = postage::barrier::channel();
-        let mut state = self.state.lock();
-        let wakeup_at = state.now + duration;
-        let id = util::post_inc(&mut state.next_timer_id);
-        match state
-            .pending_timers
-            .binary_search_by_key(&wakeup_at, |e| e.1)
-        {
-            Ok(ix) | Err(ix) => state.pending_timers.insert(ix, (id, wakeup_at, tx)),
-        }
-        let state = self.state.clone();
-        Timer::Deterministic(DeterministicTimer { rx, id, state })
-    }
-
-    pub fn now(&self) -> std::time::Instant {
-        let state = self.state.lock();
-        state.now
-    }
-
-    pub fn advance_clock(&self, duration: Duration) {
-        let new_now = self.state.lock().now + duration;
-        loop {
-            self.run_until_parked();
-            let mut state = self.state.lock();
+                        #[cfg(any(test, feature = "test-support"))]
+                        if let Some(test) = self.dispatcher.as_test() {
+                            if !test.parking_allowed() {
+                                let mut backtrace_message = String::new();
+                                if let Some(backtrace) = test.waiting_backtrace() {
+                                    backtrace_message =
+                                        format!("\nbacktrace of waiting future:\n{:?}", backtrace);
+                                }
+                                panic!("parked with nothing left to run\n{:?}", backtrace_message)
+                            }
+                        }
 
-            if let Some((_, wakeup_time, _)) = state.pending_timers.first() {
-                let wakeup_time = *wakeup_time;
-                if wakeup_time <= new_now {
-                    let timer_count = state
-                        .pending_timers
-                        .iter()
-                        .take_while(|(_, t, _)| *t == wakeup_time)
-                        .count();
-                    state.now = wakeup_time;
-                    let timers_to_wake = state
-                        .pending_timers
-                        .drain(0..timer_count)
-                        .collect::<Vec<_>>();
-                    drop(state);
-                    drop(timers_to_wake);
-                    continue;
+                        self.dispatcher.park();
+                    }
                 }
             }
-
-            break;
         }
-
-        self.state.lock().now = new_now;
     }
 
-    pub fn start_waiting(&self) {
-        self.state.lock().waiting_backtrace = Some(backtrace::Backtrace::new_unresolved());
-    }
-
-    pub fn finish_waiting(&self) {
-        self.state.lock().waiting_backtrace.take();
-    }
-
-    pub fn forbid_parking(&self) {
-        use rand::prelude::*;
-
-        let mut state = self.state.lock();
-        state.forbid_parking = true;
-        state.rng = StdRng::seed_from_u64(state.seed);
-    }
-
-    pub fn allow_parking(&self) {
-        use rand::prelude::*;
-
-        let mut state = self.state.lock();
-        state.forbid_parking = false;
-        state.rng = StdRng::seed_from_u64(state.seed);
-    }
-
-    pub async fn simulate_random_delay(&self) {
-        use rand::prelude::*;
-        use smol::future::yield_now;
-        if self.state.lock().rng.gen_bool(0.2) {
-            let yields = self.state.lock().rng.gen_range(1..=10);
-            for _ in 0..yields {
-                yield_now().await;
-            }
-        }
-    }
-
-    pub fn record_backtrace(&self) {
-        let mut state = self.state.lock();
-        if state.enable_runnable_backtraces {
-            let current_id = state
-                .poll_history
-                .iter()
-                .rev()
-                .find_map(|event| match event {
-                    ExecutorEvent::PollRunnable { id } => Some(*id),
-                    _ => None,
-                });
-            if let Some(id) = current_id {
-                state
-                    .runnable_backtraces
-                    .insert(id, backtrace::Backtrace::new_unresolved());
-            }
+    pub fn block_with_timeout<R>(
+        &self,
+        duration: Duration,
+        future: impl Future<Output = R>,
+    ) -> Result<R, impl Future<Output = R>> {
+        let mut future = Box::pin(future.fuse());
+        if duration.is_zero() {
+            return Err(future);
         }
-    }
-}
 
-impl Drop for Timer {
-    fn drop(&mut self) {
         #[cfg(any(test, feature = "test-support"))]
-        if let Timer::Deterministic(DeterministicTimer { state, id, .. }) = self {
-            state
-                .lock()
-                .pending_timers
-                .retain(|(timer_id, _, _)| timer_id != id)
-        }
-    }
-}
-
-impl Future for Timer {
-    type Output = ();
-
-    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
-        match &mut *self {
-            #[cfg(any(test, feature = "test-support"))]
-            Self::Deterministic(DeterministicTimer { rx, .. }) => {
-                use postage::stream::{PollRecv, Stream as _};
-                smol::pin!(rx);
-                match rx.poll_recv(&mut postage::Context::from_waker(cx.waker())) {
-                    PollRecv::Ready(()) | PollRecv::Closed => Poll::Ready(()),
-                    PollRecv::Pending => Poll::Pending,
-                }
+        let max_ticks = self
+            .dispatcher
+            .as_test()
+            .map_or(usize::MAX, |dispatcher| dispatcher.gen_block_on_ticks());
+        #[cfg(not(any(test, feature = "test-support")))]
+        let max_ticks = usize::MAX;
+
+        let mut timer = self.timer(duration).fuse();
+
+        let timeout = async {
+            futures::select_biased! {
+                value = future => Ok(value),
+                _ = timer => Err(()),
             }
-            Self::Production(timer) => {
-                smol::pin!(timer);
-                match timer.poll(cx) {
-                    Poll::Ready(_) => Poll::Ready(()),
-                    Poll::Pending => Poll::Pending,
-                }
-            }
-        }
-    }
-}
-
-#[cfg(any(test, feature = "test-support"))]
-impl DeterministicState {
-    fn push_to_history(&mut self, event: ExecutorEvent) {
-        use std::fmt::Write as _;
-
-        self.poll_history.push(event);
-        if let Some(prev_history) = &self.previous_poll_history {
-            let ix = self.poll_history.len() - 1;
-            let prev_event = prev_history[ix];
-            if event != prev_event {
-                let mut message = String::new();
-                writeln!(
-                    &mut message,
-                    "current runnable backtrace:\n{:?}",
-                    self.runnable_backtraces.get_mut(&event.id()).map(|trace| {
-                        trace.resolve();
-                        util::CwdBacktrace(trace)
-                    })
-                )
-                .unwrap();
-                writeln!(
-                    &mut message,
-                    "previous runnable backtrace:\n{:?}",
-                    self.runnable_backtraces
-                        .get_mut(&prev_event.id())
-                        .map(|trace| {
-                            trace.resolve();
-                            util::CwdBacktrace(trace)
-                        })
-                )
-                .unwrap();
-                panic!("detected non-determinism after {ix}. {message}");
-            }
-        }
-    }
-
-    fn will_park(&mut self) {
-        if self.forbid_parking {
-            let mut backtrace_message = String::new();
-            #[cfg(any(test, feature = "test-support"))]
-            if let Some(backtrace) = self.waiting_backtrace.as_mut() {
-                backtrace.resolve();
-                backtrace_message = format!(
-                    "\nbacktrace of waiting future:\n{:?}",
-                    util::CwdBacktrace(backtrace)
-                );
-            }
-
-            panic!(
-                "deterministic executor parked after a call to forbid_parking{}",
-                backtrace_message
-            );
-        }
-    }
-}
-
-#[cfg(any(test, feature = "test-support"))]
-impl ExecutorEvent {
-    pub fn id(&self) -> usize {
-        match self {
-            ExecutorEvent::PollRunnable { id } => *id,
-            ExecutorEvent::EnqueueRunnable { id } => *id,
+        };
+        match self.block_internal(true, timeout, max_ticks) {
+            Ok(Ok(value)) => Ok(value),
+            _ => Err(future),
         }
     }
-}
 
-impl Foreground {
-    pub fn platform(dispatcher: Arc<dyn platform::Dispatcher>) -> Result<Self> {
-        if dispatcher.is_main_thread() {
-            Ok(Self::Platform {
-                dispatcher,
-                _not_send_or_sync: PhantomData,
-            })
-        } else {
-            Err(anyhow!("must be constructed on main thread"))
+    pub async fn scoped<'scope, F>(&self, scheduler: F)
+    where
+        F: FnOnce(&mut Scope<'scope>),
+    {
+        let mut scope = Scope::new(self.clone());
+        (scheduler)(&mut scope);
+        let spawned = mem::take(&mut scope.futures)
+            .into_iter()
+            .map(|f| self.spawn(f))
+            .collect::<Vec<_>>();
+        for task in spawned {
+            task.await;
         }
     }
 
-    pub fn spawn<T: 'static>(&self, future: impl Future<Output = T> + 'static) -> Task<T> {
-        let future = any_local_future(future);
-        let any_task = match self {
-            #[cfg(any(test, feature = "test-support"))]
-            Self::Deterministic { cx_id, executor } => {
-                executor.spawn_from_foreground(*cx_id, future, false)
-            }
-            Self::Platform { dispatcher, .. } => {
-                fn spawn_inner(
-                    future: AnyLocalFuture,
-                    dispatcher: &Arc<dyn Dispatcher>,
-                ) -> AnyLocalTask {
-                    let dispatcher = dispatcher.clone();
-                    let schedule =
-                        move |runnable: Runnable| dispatcher.run_on_main_thread(runnable);
-                    let (runnable, task) = async_task::spawn_local(future, schedule);
-                    runnable.schedule();
-                    task
-                }
-                spawn_inner(future, dispatcher)
-            }
-        };
-        Task::local(any_task)
+    pub fn timer(&self, duration: Duration) -> Task<()> {
+        let (runnable, task) = async_task::spawn(async move {}, {
+            let dispatcher = self.dispatcher.clone();
+            move |runnable| dispatcher.dispatch_after(duration, runnable)
+        });
+        runnable.schedule();
+        Task::Spawned(task)
     }
 
     #[cfg(any(test, feature = "test-support"))]
-    pub fn run<T: 'static>(&self, future: impl Future<Output = T>) -> T {
-        let future = async move { Box::new(future.await) as Box<dyn Any> }.boxed_local();
-        let result = match self {
-            Self::Deterministic { cx_id, executor } => executor.run(*cx_id, future),
-            Self::Platform { .. } => panic!("you can't call run on a platform foreground executor"),
-        };
-        *result.downcast().unwrap()
+    pub fn start_waiting(&self) {
+        self.dispatcher.as_test().unwrap().start_waiting();
     }
 
     #[cfg(any(test, feature = "test-support"))]
-    pub fn run_until_parked(&self) {
-        match self {
-            Self::Deterministic { executor, .. } => executor.run_until_parked(),
-            _ => panic!("this method can only be called on a deterministic executor"),
-        }
+    pub fn finish_waiting(&self) {
+        self.dispatcher.as_test().unwrap().finish_waiting();
     }
 
     #[cfg(any(test, feature = "test-support"))]
-    pub fn parking_forbidden(&self) -> bool {
-        match self {
-            Self::Deterministic { executor, .. } => executor.state.lock().forbid_parking,
-            _ => panic!("this method can only be called on a deterministic executor"),
-        }
+    pub fn simulate_random_delay(&self) -> impl Future<Output = ()> {
+        self.dispatcher.as_test().unwrap().simulate_random_delay()
     }
 
     #[cfg(any(test, feature = "test-support"))]
-    pub fn start_waiting(&self) {
-        match self {
-            Self::Deterministic { executor, .. } => executor.start_waiting(),
-            _ => panic!("this method can only be called on a deterministic executor"),
-        }
+    pub fn deprioritize(&self, task_label: TaskLabel) {
+        self.dispatcher.as_test().unwrap().deprioritize(task_label)
     }
 
     #[cfg(any(test, feature = "test-support"))]
-    pub fn finish_waiting(&self) {
-        match self {
-            Self::Deterministic { executor, .. } => executor.finish_waiting(),
-            _ => panic!("this method can only be called on a deterministic executor"),
-        }
+    pub fn advance_clock(&self, duration: Duration) {
+        self.dispatcher.as_test().unwrap().advance_clock(duration)
     }
 
     #[cfg(any(test, feature = "test-support"))]
-    pub fn forbid_parking(&self) {
-        match self {
-            Self::Deterministic { executor, .. } => executor.forbid_parking(),
-            _ => panic!("this method can only be called on a deterministic executor"),
-        }
+    pub fn tick(&self) -> bool {
+        self.dispatcher.as_test().unwrap().tick(false)
     }
 
     #[cfg(any(test, feature = "test-support"))]
-    pub fn allow_parking(&self) {
-        match self {
-            Self::Deterministic { executor, .. } => executor.allow_parking(),
-            _ => panic!("this method can only be called on a deterministic executor"),
-        }
+    pub fn run_until_parked(&self) {
+        self.dispatcher.as_test().unwrap().run_until_parked()
     }
 
     #[cfg(any(test, feature = "test-support"))]
-    pub fn advance_clock(&self, duration: Duration) {
-        match self {
-            Self::Deterministic { executor, .. } => executor.advance_clock(duration),
-            _ => panic!("this method can only be called on a deterministic executor"),
-        }
+    pub fn allow_parking(&self) {
+        self.dispatcher.as_test().unwrap().allow_parking();
     }
 
     #[cfg(any(test, feature = "test-support"))]
-    pub fn set_block_on_ticks(&self, range: std::ops::RangeInclusive<usize>) {
-        match self {
-            Self::Deterministic { executor, .. } => executor.state.lock().block_on_ticks = range,
-            _ => panic!("this method can only be called on a deterministic executor"),
-        }
-    }
-}
-
-impl Background {
-    pub fn new() -> Self {
-        let executor = Arc::new(Executor::new());
-        let stop = channel::unbounded::<()>();
-
-        for i in 0..2 * num_cpus::get() {
-            let executor = executor.clone();
-            let stop = stop.1.clone();
-            thread::Builder::new()
-                .name(format!("background-executor-{}", i))
-                .spawn(move || smol::block_on(executor.run(stop.recv())))
-                .unwrap();
-        }
-
-        Self::Production {
-            executor,
-            _stop: stop.0,
-        }
+    pub fn rng(&self) -> StdRng {
+        self.dispatcher.as_test().unwrap().rng()
     }
 
     pub fn num_cpus(&self) -> usize {
         num_cpus::get()
     }
 
-    pub fn spawn<T, F>(&self, future: F) -> Task<T>
-    where
-        T: 'static + Send,
-        F: Send + Future<Output = T> + 'static,
-    {
-        let future = any_future(future);
-        let any_task = match self {
-            Self::Production { executor, .. } => executor.spawn(future),
-            #[cfg(any(test, feature = "test-support"))]
-            Self::Deterministic { executor } => executor.spawn(future),
-        };
-        Task::send(any_task)
+    pub fn is_main_thread(&self) -> bool {
+        self.dispatcher.is_main_thread()
     }
 
-    pub fn block<F, T>(&self, future: F) -> T
-    where
-        F: Future<Output = T>,
-    {
-        smol::pin!(future);
-        match self {
-            Self::Production { .. } => smol::block_on(&mut future),
-            #[cfg(any(test, feature = "test-support"))]
-            Self::Deterministic { executor, .. } => {
-                executor.block(&mut future, usize::MAX).unwrap()
-            }
-        }
+    #[cfg(any(test, feature = "test-support"))]
+    pub fn set_block_on_ticks(&self, range: std::ops::RangeInclusive<usize>) {
+        self.dispatcher.as_test().unwrap().set_block_on_ticks(range);
     }
+}
 
-    pub fn block_with_timeout<F, T>(
-        &self,
-        timeout: Duration,
-        future: F,
-    ) -> Result<T, impl Future<Output = T>>
-    where
-        T: 'static,
-        F: 'static + Unpin + Future<Output = T>,
-    {
-        let mut future = any_local_future(future);
-        if !timeout.is_zero() {
-            let output = match self {
-                Self::Production { .. } => smol::block_on(util::timeout(timeout, &mut future)).ok(),
-                #[cfg(any(test, feature = "test-support"))]
-                Self::Deterministic { executor, .. } => {
-                    use rand::prelude::*;
-                    let max_ticks = {
-                        let mut state = executor.state.lock();
-                        let range = state.block_on_ticks.clone();
-                        state.rng.gen_range(range)
-                    };
-                    executor.block(&mut future, max_ticks)
-                }
-            };
-            if let Some(output) = output {
-                return Ok(*output.downcast().unwrap());
-            }
+impl ForegroundExecutor {
+    pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
+        Self {
+            dispatcher,
+            not_send: PhantomData,
         }
-        Err(async { *future.await.downcast().unwrap() })
     }
 
-    pub async fn scoped<'scope, F>(self: &Arc<Self>, scheduler: F)
+    /// Enqueues the given closure to be run on any thread. The closure returns
+    /// a future which will be run to completion on any available thread.
+    pub fn spawn<R>(&self, future: impl Future<Output = R> + 'static) -> Task<R>
     where
-        F: FnOnce(&mut Scope<'scope>),
+        R: 'static,
     {
-        let mut scope = Scope::new(self.clone());
-        (scheduler)(&mut scope);
-        let spawned = mem::take(&mut scope.futures)
-            .into_iter()
-            .map(|f| self.spawn(f))
-            .collect::<Vec<_>>();
-        for task in spawned {
-            task.await;
-        }
-    }
-
-    pub fn timer(&self, duration: Duration) -> Timer {
-        match self {
-            Background::Production { .. } => Timer::Production(smol::Timer::after(duration)),
-            #[cfg(any(test, feature = "test-support"))]
-            Background::Deterministic { executor } => executor.timer(duration),
-        }
-    }
-
-    pub fn now(&self) -> std::time::Instant {
-        match self {
-            Background::Production { .. } => std::time::Instant::now(),
-            #[cfg(any(test, feature = "test-support"))]
-            Background::Deterministic { executor } => executor.now(),
-        }
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn rng<'a>(&'a self) -> impl 'a + std::ops::DerefMut<Target = rand::prelude::StdRng> {
-        match self {
-            Self::Deterministic { executor, .. } => {
-                parking_lot::lock_api::MutexGuard::map(executor.state.lock(), |s| &mut s.rng)
-            }
-            _ => panic!("this method can only be called on a deterministic executor"),
-        }
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub async fn simulate_random_delay(&self) {
-        match self {
-            Self::Deterministic { executor, .. } => {
-                executor.simulate_random_delay().await;
-            }
-            _ => {
-                panic!("this method can only be called on a deterministic executor")
-            }
-        }
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn record_backtrace(&self) {
-        match self {
-            Self::Deterministic { executor, .. } => executor.record_backtrace(),
-            _ => {
-                panic!("this method can only be called on a deterministic executor")
-            }
-        }
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn start_waiting(&self) {
-        match self {
-            Self::Deterministic { executor, .. } => executor.start_waiting(),
-            _ => panic!("this method can only be called on a deterministic executor"),
-        }
-    }
-}
-
-impl Default for Background {
-    fn default() -> Self {
-        Self::new()
+        let dispatcher = self.dispatcher.clone();
+        fn inner<R: 'static>(
+            dispatcher: Arc<dyn PlatformDispatcher>,
+            future: AnyLocalFuture<R>,
+        ) -> Task<R> {
+            let (runnable, task) = async_task::spawn_local(future, move |runnable| {
+                dispatcher.dispatch_on_main_thread(runnable)
+            });
+            runnable.schedule();
+            Task::Spawned(task)
+        }
+        inner::<R>(dispatcher, Box::pin(future))
     }
 }
 
 pub struct Scope<'a> {
-    executor: Arc<Background>,
+    executor: BackgroundExecutor,
     futures: Vec<Pin<Box<dyn Future<Output = ()> + Send + 'static>>>,
     tx: Option<mpsc::Sender<()>>,
     rx: mpsc::Receiver<()>,
-    _phantom: PhantomData<&'a ()>,
+    lifetime: PhantomData<&'a ()>,
 }
 
 impl<'a> Scope<'a> {
-    fn new(executor: Arc<Background>) -> Self {
+    fn new(executor: BackgroundExecutor) -> Self {
         let (tx, rx) = mpsc::channel(1);
         Self {
             executor,
             tx: Some(tx),
             rx,
             futures: Default::default(),
-            _phantom: PhantomData,
+            lifetime: PhantomData,
         }
     }
 

crates/gpui/src/font_cache.rs 🔗

@@ -1,330 +0,0 @@
-use crate::{
-    fonts::{Features, FontId, Metrics, Properties},
-    geometry::vector::{vec2f, Vector2F},
-    platform,
-    text_layout::LineWrapper,
-};
-use anyhow::{anyhow, Result};
-use ordered_float::OrderedFloat;
-use parking_lot::{RwLock, RwLockUpgradableReadGuard};
-use schemars::JsonSchema;
-use std::{
-    collections::HashMap,
-    ops::{Deref, DerefMut},
-    sync::Arc,
-};
-
-#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, JsonSchema)]
-pub struct FamilyId(usize);
-
-struct Family {
-    name: Arc<str>,
-    font_features: Features,
-    font_ids: Vec<FontId>,
-}
-
-pub struct FontCache(RwLock<FontCacheState>);
-
-pub struct FontCacheState {
-    font_system: Arc<dyn platform::FontSystem>,
-    families: Vec<Family>,
-    default_family: Option<FamilyId>,
-    font_selections: HashMap<FamilyId, HashMap<Properties, FontId>>,
-    metrics: HashMap<FontId, Metrics>,
-    wrapper_pool: HashMap<(FontId, OrderedFloat<f32>), Vec<LineWrapper>>,
-}
-
-pub struct LineWrapperHandle {
-    wrapper: Option<LineWrapper>,
-    font_cache: Arc<FontCache>,
-}
-
-unsafe impl Send for FontCache {}
-
-impl FontCache {
-    pub fn new(fonts: Arc<dyn platform::FontSystem>) -> Self {
-        Self(RwLock::new(FontCacheState {
-            font_system: fonts,
-            families: Default::default(),
-            default_family: None,
-            font_selections: Default::default(),
-            metrics: Default::default(),
-            wrapper_pool: Default::default(),
-        }))
-    }
-
-    pub fn family_name(&self, family_id: FamilyId) -> Result<Arc<str>> {
-        self.0
-            .read()
-            .families
-            .get(family_id.0)
-            .ok_or_else(|| anyhow!("invalid family id"))
-            .map(|family| family.name.clone())
-    }
-
-    pub fn load_family(&self, names: &[&str], features: &Features) -> Result<FamilyId> {
-        for name in names {
-            let state = self.0.upgradable_read();
-
-            if let Some(ix) = state
-                .families
-                .iter()
-                .position(|f| f.name.as_ref() == *name && f.font_features == *features)
-            {
-                return Ok(FamilyId(ix));
-            }
-
-            let mut state = RwLockUpgradableReadGuard::upgrade(state);
-
-            if let Ok(font_ids) = state.font_system.load_family(name, features) {
-                if font_ids.is_empty() {
-                    continue;
-                }
-
-                let family_id = FamilyId(state.families.len());
-                for font_id in &font_ids {
-                    if state.font_system.glyph_for_char(*font_id, 'm').is_none() {
-                        return Err(anyhow!("font must contain a glyph for the 'm' character"));
-                    }
-                }
-
-                state.families.push(Family {
-                    name: Arc::from(*name),
-                    font_features: features.clone(),
-                    font_ids,
-                });
-                return Ok(family_id);
-            }
-        }
-
-        Err(anyhow!(
-            "could not find a non-empty font family matching one of the given names: {}",
-            names
-                .iter()
-                .map(|name| format!("`{name}`"))
-                .collect::<Vec<_>>()
-                .join(", ")
-        ))
-    }
-
-    /// Returns an arbitrary font family that is available on the system.
-    pub fn known_existing_family(&self) -> FamilyId {
-        if let Some(family_id) = self.0.read().default_family {
-            return family_id;
-        }
-
-        let default_family = self
-            .load_family(
-                &["Courier", "Helvetica", "Arial", "Verdana"],
-                &Default::default(),
-            )
-            .unwrap_or_else(|_| {
-                let all_family_names = self.0.read().font_system.all_families();
-                let all_family_names: Vec<_> = all_family_names
-                    .iter()
-                    .map(|string| string.as_str())
-                    .collect();
-                self.load_family(&all_family_names, &Default::default())
-                    .expect("could not load any default font family")
-            });
-
-        self.0.write().default_family = Some(default_family);
-        default_family
-    }
-
-    pub fn default_font(&self, family_id: FamilyId) -> FontId {
-        self.select_font(family_id, &Properties::default()).unwrap()
-    }
-
-    pub fn select_font(&self, family_id: FamilyId, properties: &Properties) -> Result<FontId> {
-        let inner = self.0.upgradable_read();
-        if let Some(font_id) = inner
-            .font_selections
-            .get(&family_id)
-            .and_then(|f| f.get(properties))
-        {
-            Ok(*font_id)
-        } else {
-            let mut inner = RwLockUpgradableReadGuard::upgrade(inner);
-            let family = &inner.families[family_id.0];
-            let font_id = inner
-                .font_system
-                .select_font(&family.font_ids, properties)
-                .unwrap_or(family.font_ids[0]);
-
-            inner
-                .font_selections
-                .entry(family_id)
-                .or_default()
-                .insert(*properties, font_id);
-            Ok(font_id)
-        }
-    }
-
-    pub fn metric<F, T>(&self, font_id: FontId, f: F) -> T
-    where
-        F: FnOnce(&Metrics) -> T,
-        T: 'static,
-    {
-        let state = self.0.upgradable_read();
-        if let Some(metrics) = state.metrics.get(&font_id) {
-            f(metrics)
-        } else {
-            let metrics = state.font_system.font_metrics(font_id);
-            let metric = f(&metrics);
-            let mut state = RwLockUpgradableReadGuard::upgrade(state);
-            state.metrics.insert(font_id, metrics);
-            metric
-        }
-    }
-
-    pub fn bounding_box(&self, font_id: FontId, font_size: f32) -> Vector2F {
-        let bounding_box = self.metric(font_id, |m| m.bounding_box);
-        let width = bounding_box.width() * self.em_scale(font_id, font_size);
-        let height = bounding_box.height() * self.em_scale(font_id, font_size);
-        vec2f(width, height)
-    }
-
-    pub fn em_width(&self, font_id: FontId, font_size: f32) -> f32 {
-        let glyph_id;
-        let bounds;
-        {
-            let state = self.0.read();
-            glyph_id = state.font_system.glyph_for_char(font_id, 'm').unwrap();
-            bounds = state
-                .font_system
-                .typographic_bounds(font_id, glyph_id)
-                .unwrap();
-        }
-        bounds.width() * self.em_scale(font_id, font_size)
-    }
-
-    pub fn em_advance(&self, font_id: FontId, font_size: f32) -> f32 {
-        let glyph_id;
-        let advance;
-        {
-            let state = self.0.read();
-            glyph_id = state.font_system.glyph_for_char(font_id, 'm').unwrap();
-            advance = state.font_system.advance(font_id, glyph_id).unwrap();
-        }
-        advance.x() * self.em_scale(font_id, font_size)
-    }
-
-    pub fn line_height(&self, font_size: f32) -> f32 {
-        (font_size * 1.618).round()
-    }
-
-    pub fn cap_height(&self, font_id: FontId, font_size: f32) -> f32 {
-        self.metric(font_id, |m| m.cap_height) * self.em_scale(font_id, font_size)
-    }
-
-    pub fn x_height(&self, font_id: FontId, font_size: f32) -> f32 {
-        self.metric(font_id, |m| m.x_height) * self.em_scale(font_id, font_size)
-    }
-
-    pub fn ascent(&self, font_id: FontId, font_size: f32) -> f32 {
-        self.metric(font_id, |m| m.ascent) * self.em_scale(font_id, font_size)
-    }
-
-    pub fn descent(&self, font_id: FontId, font_size: f32) -> f32 {
-        self.metric(font_id, |m| -m.descent) * self.em_scale(font_id, font_size)
-    }
-
-    pub fn em_scale(&self, font_id: FontId, font_size: f32) -> f32 {
-        font_size / self.metric(font_id, |m| m.units_per_em as f32)
-    }
-
-    pub fn baseline_offset(&self, font_id: FontId, font_size: f32) -> f32 {
-        let line_height = self.line_height(font_size);
-        let ascent = self.ascent(font_id, font_size);
-        let descent = self.descent(font_id, font_size);
-        let padding_top = (line_height - ascent - descent) / 2.;
-        padding_top + ascent
-    }
-
-    pub fn line_wrapper(self: &Arc<Self>, font_id: FontId, font_size: f32) -> LineWrapperHandle {
-        let mut state = self.0.write();
-        let wrappers = state
-            .wrapper_pool
-            .entry((font_id, OrderedFloat(font_size)))
-            .or_default();
-        let wrapper = wrappers
-            .pop()
-            .unwrap_or_else(|| LineWrapper::new(font_id, font_size, state.font_system.clone()));
-        LineWrapperHandle {
-            wrapper: Some(wrapper),
-            font_cache: self.clone(),
-        }
-    }
-}
-
-impl Drop for LineWrapperHandle {
-    fn drop(&mut self) {
-        let mut state = self.font_cache.0.write();
-        let wrapper = self.wrapper.take().unwrap();
-        state
-            .wrapper_pool
-            .get_mut(&(wrapper.font_id, OrderedFloat(wrapper.font_size)))
-            .unwrap()
-            .push(wrapper);
-    }
-}
-
-impl Deref for LineWrapperHandle {
-    type Target = LineWrapper;
-
-    fn deref(&self) -> &Self::Target {
-        self.wrapper.as_ref().unwrap()
-    }
-}
-
-impl DerefMut for LineWrapperHandle {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        self.wrapper.as_mut().unwrap()
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::{
-        fonts::{Style, Weight},
-        platform::{test, Platform as _},
-    };
-
-    #[test]
-    fn test_select_font() {
-        let platform = test::platform();
-        let fonts = FontCache::new(platform.fonts());
-        let arial = fonts
-            .load_family(
-                &["Arial"],
-                &Features {
-                    calt: Some(false),
-                    ..Default::default()
-                },
-            )
-            .unwrap();
-        let arial_regular = fonts.select_font(arial, &Properties::new()).unwrap();
-        let arial_italic = fonts
-            .select_font(arial, Properties::new().style(Style::Italic))
-            .unwrap();
-        let arial_bold = fonts
-            .select_font(arial, Properties::new().weight(Weight::BOLD))
-            .unwrap();
-        assert_ne!(arial_regular, arial_italic);
-        assert_ne!(arial_regular, arial_bold);
-        assert_ne!(arial_italic, arial_bold);
-
-        let arial_with_calt = fonts
-            .load_family(
-                &["Arial"],
-                &Features {
-                    calt: Some(true),
-                    ..Default::default()
-                },
-            )
-            .unwrap();
-        assert_ne!(arial_with_calt, arial);
-    }
-}

crates/gpui/src/fonts.rs 🔗

@@ -1,636 +0,0 @@
-use crate::{
-    color::Color,
-    font_cache::FamilyId,
-    json::{json, ToJson},
-    text_layout::RunStyle,
-    FontCache,
-};
-use anyhow::{anyhow, Result};
-pub use font_kit::{
-    metrics::Metrics,
-    properties::{Properties, Stretch, Style, Weight},
-};
-use ordered_float::OrderedFloat;
-use refineable::Refineable;
-use schemars::JsonSchema;
-use serde::{de, Deserialize, Serialize};
-use serde_json::Value;
-use std::{cell::RefCell, sync::Arc};
-
-#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, JsonSchema)]
-pub struct FontId(pub usize);
-
-pub type GlyphId = u32;
-
-#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
-pub struct Features {
-    pub calt: Option<bool>,
-    pub case: Option<bool>,
-    pub cpsp: Option<bool>,
-    pub frac: Option<bool>,
-    pub liga: Option<bool>,
-    pub onum: Option<bool>,
-    pub ordn: Option<bool>,
-    pub pnum: Option<bool>,
-    pub ss01: Option<bool>,
-    pub ss02: Option<bool>,
-    pub ss03: Option<bool>,
-    pub ss04: Option<bool>,
-    pub ss05: Option<bool>,
-    pub ss06: Option<bool>,
-    pub ss07: Option<bool>,
-    pub ss08: Option<bool>,
-    pub ss09: Option<bool>,
-    pub ss10: Option<bool>,
-    pub ss11: Option<bool>,
-    pub ss12: Option<bool>,
-    pub ss13: Option<bool>,
-    pub ss14: Option<bool>,
-    pub ss15: Option<bool>,
-    pub ss16: Option<bool>,
-    pub ss17: Option<bool>,
-    pub ss18: Option<bool>,
-    pub ss19: Option<bool>,
-    pub ss20: Option<bool>,
-    pub subs: Option<bool>,
-    pub sups: Option<bool>,
-    pub swsh: Option<bool>,
-    pub titl: Option<bool>,
-    pub tnum: Option<bool>,
-    pub zero: Option<bool>,
-}
-
-#[derive(Clone, Debug, JsonSchema)]
-pub struct TextStyle {
-    pub color: Color,
-    pub font_family_name: Arc<str>,
-    pub font_family_id: FamilyId,
-    pub font_id: FontId,
-    pub font_size: f32,
-    #[schemars(with = "PropertiesDef")]
-    pub font_properties: Properties,
-    pub underline: Underline,
-    pub soft_wrap: bool,
-}
-
-impl TextStyle {
-    pub fn for_color(color: Color) -> Self {
-        Self {
-            color,
-            ..Default::default()
-        }
-    }
-}
-
-impl TextStyle {
-    pub fn refine(
-        &mut self,
-        refinement: &TextStyleRefinement,
-        font_cache: &FontCache,
-    ) -> Result<()> {
-        if let Some(font_size) = refinement.font_size {
-            self.font_size = font_size;
-        }
-        if let Some(color) = refinement.color {
-            self.color = color;
-        }
-        if let Some(underline) = refinement.underline {
-            self.underline = underline;
-        }
-
-        let mut update_font_id = false;
-        if let Some(font_family) = refinement.font_family.clone() {
-            self.font_family_id = font_cache.load_family(&[&font_family], &Default::default())?;
-            self.font_family_name = font_family;
-            update_font_id = true;
-        }
-        if let Some(font_weight) = refinement.font_weight {
-            self.font_properties.weight = font_weight;
-            update_font_id = true;
-        }
-        if let Some(font_style) = refinement.font_style {
-            self.font_properties.style = font_style;
-            update_font_id = true;
-        }
-
-        if update_font_id {
-            self.font_id = font_cache.select_font(self.font_family_id, &self.font_properties)?;
-        }
-
-        Ok(())
-    }
-}
-
-#[derive(Clone, Debug, Default)]
-pub struct TextStyleRefinement {
-    pub color: Option<Color>,
-    pub font_family: Option<Arc<str>>,
-    pub font_size: Option<f32>,
-    pub font_weight: Option<Weight>,
-    pub font_style: Option<Style>,
-    pub underline: Option<Underline>,
-}
-
-impl Refineable for TextStyleRefinement {
-    type Refinement = Self;
-
-    fn refine(&mut self, refinement: &Self::Refinement) {
-        if refinement.color.is_some() {
-            self.color = refinement.color;
-        }
-        if refinement.font_family.is_some() {
-            self.font_family = refinement.font_family.clone();
-        }
-        if refinement.font_size.is_some() {
-            self.font_size = refinement.font_size;
-        }
-        if refinement.font_weight.is_some() {
-            self.font_weight = refinement.font_weight;
-        }
-        if refinement.font_style.is_some() {
-            self.font_style = refinement.font_style;
-        }
-        if refinement.underline.is_some() {
-            self.underline = refinement.underline;
-        }
-    }
-
-    fn refined(mut self, refinement: Self::Refinement) -> Self {
-        self.refine(&refinement);
-        self
-    }
-}
-
-#[derive(JsonSchema)]
-#[serde(remote = "Properties")]
-pub struct PropertiesDef {
-    /// The font style, as defined in CSS.
-    pub style: StyleDef,
-    /// The font weight, as defined in CSS.
-    pub weight: f32,
-    /// The font stretchiness, as defined in CSS.
-    pub stretch: f32,
-}
-
-#[derive(JsonSchema)]
-#[schemars(remote = "Style")]
-pub enum StyleDef {
-    /// A face that is neither italic not obliqued.
-    Normal,
-    /// A form that is generally cursive in nature.
-    Italic,
-    /// A typically-sloped version of the regular face.
-    Oblique,
-}
-
-#[derive(Copy, Clone, Debug, Default, PartialEq, JsonSchema)]
-pub struct HighlightStyle {
-    pub color: Option<Color>,
-    #[schemars(with = "Option::<f32>")]
-    pub weight: Option<Weight>,
-    pub italic: Option<bool>,
-    pub underline: Option<Underline>,
-    pub fade_out: Option<f32>,
-}
-
-impl Eq for HighlightStyle {}
-
-#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, JsonSchema)]
-pub struct Underline {
-    pub color: Option<Color>,
-    #[schemars(with = "f32")]
-    pub thickness: OrderedFloat<f32>,
-    pub squiggly: bool,
-}
-
-#[allow(non_camel_case_types)]
-#[derive(Deserialize)]
-enum WeightJson {
-    thin,
-    extra_light,
-    light,
-    normal,
-    medium,
-    semibold,
-    bold,
-    extra_bold,
-    black,
-}
-
-thread_local! {
-    static FONT_CACHE: RefCell<Option<Arc<FontCache>>> = Default::default();
-}
-
-#[derive(Deserialize)]
-struct TextStyleJson {
-    color: Color,
-    family: String,
-    #[serde(default)]
-    features: Features,
-    weight: Option<WeightJson>,
-    size: f32,
-    #[serde(default)]
-    italic: bool,
-    #[serde(default)]
-    underline: UnderlineStyleJson,
-}
-
-#[derive(Deserialize)]
-struct HighlightStyleJson {
-    color: Option<Color>,
-    weight: Option<WeightJson>,
-    italic: Option<bool>,
-    underline: Option<UnderlineStyleJson>,
-    fade_out: Option<f32>,
-}
-
-#[derive(Deserialize)]
-#[serde(untagged)]
-enum UnderlineStyleJson {
-    Underlined(bool),
-    UnderlinedWithProperties {
-        #[serde(default)]
-        color: Option<Color>,
-        #[serde(default)]
-        thickness: Option<f32>,
-        #[serde(default)]
-        squiggly: bool,
-    },
-}
-
-impl TextStyle {
-    pub fn new(
-        font_family_name: impl Into<Arc<str>>,
-        font_size: f32,
-        font_properties: Properties,
-        font_features: Features,
-        underline: Underline,
-        color: Color,
-        font_cache: &FontCache,
-    ) -> Result<Self> {
-        let font_family_name = font_family_name.into();
-        let font_family_id = font_cache.load_family(&[&font_family_name], &font_features)?;
-        let font_id = font_cache.select_font(font_family_id, &font_properties)?;
-        Ok(Self {
-            color,
-            font_family_name,
-            font_family_id,
-            font_id,
-            font_size,
-            font_properties,
-            underline,
-            soft_wrap: false,
-        })
-    }
-
-    pub fn default(font_cache: &FontCache) -> Self {
-        let font_family_id = font_cache.known_existing_family();
-        let font_id = font_cache
-            .select_font(font_family_id, &Default::default())
-            .expect("did not have any font in system-provided family");
-        let font_family_name = font_cache
-            .family_name(font_family_id)
-            .expect("we loaded this family from the font cache, so this should work");
-
-        Self {
-            color: Color::default(),
-            font_family_name,
-            font_family_id,
-            font_id,
-            font_size: 14.,
-            font_properties: Default::default(),
-            underline: Default::default(),
-            soft_wrap: true,
-        }
-    }
-
-    pub fn with_font_size(mut self, font_size: f32) -> Self {
-        self.font_size = font_size;
-        self
-    }
-
-    pub fn highlight(mut self, style: HighlightStyle, font_cache: &FontCache) -> Result<Self> {
-        let mut font_properties = self.font_properties;
-        if let Some(weight) = style.weight {
-            font_properties.weight(weight);
-        }
-        if let Some(italic) = style.italic {
-            if italic {
-                font_properties.style(Style::Italic);
-            } else {
-                font_properties.style(Style::Normal);
-            }
-        }
-
-        if self.font_properties != font_properties {
-            self.font_id = font_cache.select_font(self.font_family_id, &font_properties)?;
-        }
-        if let Some(color) = style.color {
-            self.color = Color::blend(color, self.color);
-        }
-        if let Some(factor) = style.fade_out {
-            self.color.fade_out(factor);
-        }
-        if let Some(underline) = style.underline {
-            self.underline = underline;
-        }
-
-        Ok(self)
-    }
-
-    pub fn to_run(&self) -> RunStyle {
-        RunStyle {
-            font_id: self.font_id,
-            color: self.color,
-            underline: self.underline,
-        }
-    }
-
-    fn from_json(json: TextStyleJson) -> Result<Self> {
-        FONT_CACHE.with(|font_cache| {
-            if let Some(font_cache) = font_cache.borrow().as_ref() {
-                let font_properties = properties_from_json(json.weight, json.italic);
-                Self::new(
-                    json.family,
-                    json.size,
-                    font_properties,
-                    json.features,
-                    underline_from_json(json.underline),
-                    json.color,
-                    font_cache,
-                )
-            } else {
-                Err(anyhow!(
-                    "TextStyle can only be deserialized within a call to with_font_cache"
-                ))
-            }
-        })
-    }
-
-    pub fn line_height(&self, font_cache: &FontCache) -> f32 {
-        font_cache.line_height(self.font_size)
-    }
-
-    pub fn cap_height(&self, font_cache: &FontCache) -> f32 {
-        font_cache.cap_height(self.font_id, self.font_size)
-    }
-
-    pub fn x_height(&self, font_cache: &FontCache) -> f32 {
-        font_cache.x_height(self.font_id, self.font_size)
-    }
-
-    pub fn em_width(&self, font_cache: &FontCache) -> f32 {
-        font_cache.em_width(self.font_id, self.font_size)
-    }
-
-    pub fn em_advance(&self, font_cache: &FontCache) -> f32 {
-        font_cache.em_advance(self.font_id, self.font_size)
-    }
-
-    pub fn descent(&self, font_cache: &FontCache) -> f32 {
-        font_cache.metric(self.font_id, |m| m.descent) * self.em_scale(font_cache)
-    }
-
-    pub fn baseline_offset(&self, font_cache: &FontCache) -> f32 {
-        font_cache.baseline_offset(self.font_id, self.font_size)
-    }
-
-    fn em_scale(&self, font_cache: &FontCache) -> f32 {
-        font_cache.em_scale(self.font_id, self.font_size)
-    }
-}
-
-impl From<TextStyle> for HighlightStyle {
-    fn from(other: TextStyle) -> Self {
-        Self::from(&other)
-    }
-}
-
-impl From<&TextStyle> for HighlightStyle {
-    fn from(other: &TextStyle) -> Self {
-        Self {
-            color: Some(other.color),
-            weight: Some(other.font_properties.weight),
-            italic: Some(other.font_properties.style == Style::Italic),
-            underline: Some(other.underline),
-            fade_out: None,
-        }
-    }
-}
-
-impl Default for UnderlineStyleJson {
-    fn default() -> Self {
-        Self::Underlined(false)
-    }
-}
-
-impl Default for TextStyle {
-    fn default() -> Self {
-        FONT_CACHE.with(|font_cache| {
-            let font_cache = font_cache.borrow();
-            let font_cache = font_cache
-                .as_ref()
-                .expect("TextStyle::default can only be called within a call to with_font_cache");
-            Self::default(font_cache)
-        })
-    }
-}
-
-impl HighlightStyle {
-    fn from_json(json: HighlightStyleJson) -> Self {
-        Self {
-            color: json.color,
-            weight: json.weight.map(weight_from_json),
-            italic: json.italic,
-            underline: json.underline.map(underline_from_json),
-            fade_out: json.fade_out,
-        }
-    }
-
-    pub fn highlight(&mut self, other: HighlightStyle) {
-        match (self.color, other.color) {
-            (Some(self_color), Some(other_color)) => {
-                self.color = Some(Color::blend(other_color, self_color));
-            }
-            (None, Some(other_color)) => {
-                self.color = Some(other_color);
-            }
-            _ => {}
-        }
-
-        if other.weight.is_some() {
-            self.weight = other.weight;
-        }
-
-        if other.italic.is_some() {
-            self.italic = other.italic;
-        }
-
-        if other.underline.is_some() {
-            self.underline = other.underline;
-        }
-
-        match (other.fade_out, self.fade_out) {
-            (Some(source_fade), None) => self.fade_out = Some(source_fade),
-            (Some(source_fade), Some(dest_fade)) => {
-                let source_alpha = 1. - source_fade;
-                let dest_alpha = 1. - dest_fade;
-                let blended_alpha = source_alpha + (dest_alpha * source_fade);
-                let blended_fade = 1. - blended_alpha;
-                self.fade_out = Some(blended_fade);
-            }
-            _ => {}
-        }
-    }
-}
-
-impl From<Color> for HighlightStyle {
-    fn from(color: Color) -> Self {
-        Self {
-            color: Some(color),
-            ..Default::default()
-        }
-    }
-}
-
-impl<'de> Deserialize<'de> for TextStyle {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: serde::Deserializer<'de>,
-    {
-        Self::from_json(TextStyleJson::deserialize(deserializer)?).map_err(de::Error::custom)
-    }
-}
-
-impl ToJson for TextStyle {
-    fn to_json(&self) -> Value {
-        json!({
-            "color": self.color.to_json(),
-            "font_family": self.font_family_name.as_ref(),
-            "font_properties": self.font_properties.to_json(),
-        })
-    }
-}
-
-impl<'de> Deserialize<'de> for HighlightStyle {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: serde::Deserializer<'de>,
-    {
-        let json = serde_json::Value::deserialize(deserializer)?;
-        if json.is_object() {
-            Ok(Self::from_json(
-                serde_json::from_value(json).map_err(de::Error::custom)?,
-            ))
-        } else {
-            Ok(Self {
-                color: serde_json::from_value(json).map_err(de::Error::custom)?,
-                ..Default::default()
-            })
-        }
-    }
-}
-
-fn underline_from_json(json: UnderlineStyleJson) -> Underline {
-    match json {
-        UnderlineStyleJson::Underlined(false) => Underline::default(),
-        UnderlineStyleJson::Underlined(true) => Underline {
-            color: None,
-            thickness: 1.0.into(),
-            squiggly: false,
-        },
-        UnderlineStyleJson::UnderlinedWithProperties {
-            color,
-            thickness,
-            squiggly,
-        } => Underline {
-            color,
-            thickness: thickness.unwrap_or(1.).into(),
-            squiggly,
-        },
-    }
-}
-
-fn properties_from_json(weight: Option<WeightJson>, italic: bool) -> Properties {
-    let weight = weight.map(weight_from_json).unwrap_or_default();
-    let style = if italic { Style::Italic } else { Style::Normal };
-    *Properties::new().weight(weight).style(style)
-}
-
-fn weight_from_json(weight: WeightJson) -> Weight {
-    match weight {
-        WeightJson::thin => Weight::THIN,
-        WeightJson::extra_light => Weight::EXTRA_LIGHT,
-        WeightJson::light => Weight::LIGHT,
-        WeightJson::normal => Weight::NORMAL,
-        WeightJson::medium => Weight::MEDIUM,
-        WeightJson::semibold => Weight::SEMIBOLD,
-        WeightJson::bold => Weight::BOLD,
-        WeightJson::extra_bold => Weight::EXTRA_BOLD,
-        WeightJson::black => Weight::BLACK,
-    }
-}
-
-impl ToJson for Properties {
-    fn to_json(&self) -> crate::json::Value {
-        json!({
-            "style": self.style.to_json(),
-            "weight": self.weight.to_json(),
-            "stretch": self.stretch.to_json(),
-        })
-    }
-}
-
-impl ToJson for Style {
-    fn to_json(&self) -> crate::json::Value {
-        match self {
-            Style::Normal => json!("normal"),
-            Style::Italic => json!("italic"),
-            Style::Oblique => json!("oblique"),
-        }
-    }
-}
-
-impl ToJson for Weight {
-    fn to_json(&self) -> crate::json::Value {
-        if self.0 == Weight::THIN.0 {
-            json!("thin")
-        } else if self.0 == Weight::EXTRA_LIGHT.0 {
-            json!("extra light")
-        } else if self.0 == Weight::LIGHT.0 {
-            json!("light")
-        } else if self.0 == Weight::NORMAL.0 {
-            json!("normal")
-        } else if self.0 == Weight::MEDIUM.0 {
-            json!("medium")
-        } else if self.0 == Weight::SEMIBOLD.0 {
-            json!("semibold")
-        } else if self.0 == Weight::BOLD.0 {
-            json!("bold")
-        } else if self.0 == Weight::EXTRA_BOLD.0 {
-            json!("extra bold")
-        } else if self.0 == Weight::BLACK.0 {
-            json!("black")
-        } else {
-            json!(self.0)
-        }
-    }
-}
-
-impl ToJson for Stretch {
-    fn to_json(&self) -> serde_json::Value {
-        json!(self.0)
-    }
-}
-
-pub fn with_font_cache<F, T>(font_cache: Arc<FontCache>, callback: F) -> T
-where
-    F: FnOnce() -> T,
-{
-    FONT_CACHE.with(|cache| {
-        *cache.borrow_mut() = Some(font_cache);
-        let result = callback();
-        cache.borrow_mut().take();
-        result
-    })
-}

crates/gpui/src/geometry.rs 🔗

@@ -1,407 +1,2484 @@
-use super::scene::{Path, PathVertex};
-use crate::{color::Color, json::ToJson};
-use derive_more::Neg;
-pub use pathfinder_geometry::*;
-use rect::RectF;
+use core::fmt::Debug;
+use derive_more::{Add, AddAssign, Div, DivAssign, Mul, Neg, Sub, SubAssign};
 use refineable::Refineable;
-use serde::{Deserialize, Deserializer};
-use serde_json::json;
-use std::fmt::Debug;
-use vector::{vec2f, Vector2F};
+use serde_derive::{Deserialize, Serialize};
+use std::{
+    cmp::{self, PartialOrd},
+    fmt,
+    ops::{Add, Div, Mul, MulAssign, Sub},
+};
 
-pub struct PathBuilder {
-    vertices: Vec<PathVertex>,
-    start: Vector2F,
-    current: Vector2F,
-    contour_count: usize,
-    bounds: RectF,
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum Axis {
+    Vertical,
+    Horizontal,
 }
 
-enum PathVertexKind {
-    Solid,
-    Quadratic,
+impl Axis {
+    pub fn invert(&self) -> Self {
+        match self {
+            Axis::Vertical => Axis::Horizontal,
+            Axis::Horizontal => Axis::Vertical,
+        }
+    }
+}
+
+pub trait Along {
+    type Unit;
+
+    fn along(&self, axis: Axis) -> Self::Unit;
+
+    fn apply_along(&self, axis: Axis, f: impl FnOnce(Self::Unit) -> Self::Unit) -> Self;
+}
+
+impl sqlez::bindable::StaticColumnCount for Axis {}
+impl sqlez::bindable::Bind for Axis {
+    fn bind(
+        &self,
+        statement: &sqlez::statement::Statement,
+        start_index: i32,
+    ) -> anyhow::Result<i32> {
+        match self {
+            Axis::Horizontal => "Horizontal",
+            Axis::Vertical => "Vertical",
+        }
+        .bind(statement, start_index)
+    }
+}
+
+impl sqlez::bindable::Column for Axis {
+    fn column(
+        statement: &mut sqlez::statement::Statement,
+        start_index: i32,
+    ) -> anyhow::Result<(Self, i32)> {
+        String::column(statement, start_index).and_then(|(axis_text, next_index)| {
+            Ok((
+                match axis_text.as_str() {
+                    "Horizontal" => Axis::Horizontal,
+                    "Vertical" => Axis::Vertical,
+                    _ => anyhow::bail!("Stored serialized item kind is incorrect"),
+                },
+                next_index,
+            ))
+        })
+    }
+}
+
+/// Describes a location in a 2D cartesian coordinate space.
+///
+/// It holds two public fields, `x` and `y`, which represent the coordinates in the space.
+/// The type `T` for the coordinates can be any type that implements `Default`, `Clone`, and `Debug`.
+///
+/// # Examples
+///
+/// ```
+/// # use zed::Point;
+/// let point = Point { x: 10, y: 20 };
+/// println!("{:?}", point); // Outputs: Point { x: 10, y: 20 }
+/// ```
+#[derive(Refineable, Default, Add, AddAssign, Sub, SubAssign, Copy, Debug, PartialEq, Eq, Hash)]
+#[refineable(Debug)]
+#[repr(C)]
+pub struct Point<T: Default + Clone + Debug> {
+    pub x: T,
+    pub y: T,
+}
+
+/// Constructs a new `Point<T>` with the given x and y coordinates.
+///
+/// # Arguments
+///
+/// * `x` - The x coordinate of the point.
+/// * `y` - The y coordinate of the point.
+///
+/// # Returns
+///
+/// Returns a `Point<T>` with the specified coordinates.
+///
+/// # Examples
+///
+/// ```
+/// # use zed::Point;
+/// let p = point(10, 20);
+/// assert_eq!(p.x, 10);
+/// assert_eq!(p.y, 20);
+/// ```
+pub fn point<T: Clone + Debug + Default>(x: T, y: T) -> Point<T> {
+    Point { x, y }
+}
+
+impl<T: Clone + Debug + Default> Point<T> {
+    /// Creates a new `Point` with the specified `x` and `y` coordinates.
+    ///
+    /// # Arguments
+    ///
+    /// * `x` - The horizontal coordinate of the point.
+    /// * `y` - The vertical coordinate of the point.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// let p = Point::new(10, 20);
+    /// assert_eq!(p.x, 10);
+    /// assert_eq!(p.y, 20);
+    /// ```
+    pub const fn new(x: T, y: T) -> Self {
+        Self { x, y }
+    }
+
+    /// Transforms the point to a `Point<U>` by applying the given function to both coordinates.
+    ///
+    /// This method allows for converting a `Point<T>` to a `Point<U>` by specifying a closure
+    /// that defines how to convert between the two types. The closure is applied to both the `x`
+    /// and `y` coordinates, resulting in a new point of the desired type.
+    ///
+    /// # Arguments
+    ///
+    /// * `f` - A closure that takes a value of type `T` and returns a value of type `U`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::Point;
+    /// let p = Point { x: 3, y: 4 };
+    /// let p_float = p.map(|coord| coord as f32);
+    /// assert_eq!(p_float, Point { x: 3.0, y: 4.0 });
+    /// ```
+    pub fn map<U: Clone + Default + Debug>(&self, f: impl Fn(T) -> U) -> Point<U> {
+        Point {
+            x: f(self.x.clone()),
+            y: f(self.y.clone()),
+        }
+    }
+}
+
+impl<T: Clone + Debug + Default> Along for Point<T> {
+    type Unit = T;
+
+    fn along(&self, axis: Axis) -> T {
+        match axis {
+            Axis::Horizontal => self.x.clone(),
+            Axis::Vertical => self.y.clone(),
+        }
+    }
+
+    fn apply_along(&self, axis: Axis, f: impl FnOnce(T) -> T) -> Point<T> {
+        match axis {
+            Axis::Horizontal => Point {
+                x: f(self.x.clone()),
+                y: self.y.clone(),
+            },
+            Axis::Vertical => Point {
+                x: self.x.clone(),
+                y: f(self.y.clone()),
+            },
+        }
+    }
+}
+
+impl Point<Pixels> {
+    /// Scales the point by a given factor, which is typically derived from the resolution
+    /// of a target display to ensure proper sizing of UI elements.
+    ///
+    /// # Arguments
+    ///
+    /// * `factor` - The scaling factor to apply to both the x and y coordinates.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::{Point, Pixels, ScaledPixels};
+    /// let p = Point { x: Pixels(10.0), y: Pixels(20.0) };
+    /// let scaled_p = p.scale(1.5);
+    /// assert_eq!(scaled_p, Point { x: ScaledPixels(15.0), y: ScaledPixels(30.0) });
+    /// ```
+    pub fn scale(&self, factor: f32) -> Point<ScaledPixels> {
+        Point {
+            x: self.x.scale(factor),
+            y: self.y.scale(factor),
+        }
+    }
+
+    /// Calculates the Euclidean distance from the origin (0, 0) to this point.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::Point;
+    /// # use zed::Pixels;
+    /// let p = Point { x: Pixels(3.0), y: Pixels(4.0) };
+    /// assert_eq!(p.magnitude(), 5.0);
+    /// ```
+    pub fn magnitude(&self) -> f64 {
+        ((self.x.0.powi(2) + self.y.0.powi(2)) as f64).sqrt()
+    }
+}
+
+impl<T, Rhs> Mul<Rhs> for Point<T>
+where
+    T: Mul<Rhs, Output = T> + Clone + Default + Debug,
+    Rhs: Clone + Debug,
+{
+    type Output = Point<T>;
+
+    fn mul(self, rhs: Rhs) -> Self::Output {
+        Point {
+            x: self.x * rhs.clone(),
+            y: self.y * rhs,
+        }
+    }
+}
+
+impl<T, S> MulAssign<S> for Point<T>
+where
+    T: Clone + Mul<S, Output = T> + Default + Debug,
+    S: Clone,
+{
+    fn mul_assign(&mut self, rhs: S) {
+        self.x = self.x.clone() * rhs.clone();
+        self.y = self.y.clone() * rhs;
+    }
+}
+
+impl<T, S> Div<S> for Point<T>
+where
+    T: Div<S, Output = T> + Clone + Default + Debug,
+    S: Clone,
+{
+    type Output = Self;
+
+    fn div(self, rhs: S) -> Self::Output {
+        Self {
+            x: self.x / rhs.clone(),
+            y: self.y / rhs,
+        }
+    }
+}
+
+impl<T> Point<T>
+where
+    T: PartialOrd + Clone + Default + Debug,
+{
+    /// Returns a new point with the maximum values of each dimension from `self` and `other`.
+    ///
+    /// # Arguments
+    ///
+    /// * `other` - A reference to another `Point` to compare with `self`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::Point;
+    /// let p1 = Point { x: 3, y: 7 };
+    /// let p2 = Point { x: 5, y: 2 };
+    /// let max_point = p1.max(&p2);
+    /// assert_eq!(max_point, Point { x: 5, y: 7 });
+    /// ```
+    pub fn max(&self, other: &Self) -> Self {
+        Point {
+            x: if self.x > other.x {
+                self.x.clone()
+            } else {
+                other.x.clone()
+            },
+            y: if self.y > other.y {
+                self.y.clone()
+            } else {
+                other.y.clone()
+            },
+        }
+    }
+
+    /// Returns a new point with the minimum values of each dimension from `self` and `other`.
+    ///
+    /// # Arguments
+    ///
+    /// * `other` - A reference to another `Point` to compare with `self`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::Point;
+    /// let p1 = Point { x: 3, y: 7 };
+    /// let p2 = Point { x: 5, y: 2 };
+    /// let min_point = p1.min(&p2);
+    /// assert_eq!(min_point, Point { x: 3, y: 2 });
+    /// ```
+    pub fn min(&self, other: &Self) -> Self {
+        Point {
+            x: if self.x <= other.x {
+                self.x.clone()
+            } else {
+                other.x.clone()
+            },
+            y: if self.y <= other.y {
+                self.y.clone()
+            } else {
+                other.y.clone()
+            },
+        }
+    }
+
+    /// Clamps the point to a specified range.
+    ///
+    /// Given a minimum point and a maximum point, this method constrains the current point
+    /// such that its coordinates do not exceed the range defined by the minimum and maximum points.
+    /// If the current point's coordinates are less than the minimum, they are set to the minimum.
+    /// If they are greater than the maximum, they are set to the maximum.
+    ///
+    /// # Arguments
+    ///
+    /// * `min` - A reference to a `Point` representing the minimum allowable coordinates.
+    /// * `max` - A reference to a `Point` representing the maximum allowable coordinates.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::Point;
+    /// let p = Point { x: 10, y: 20 };
+    /// let min = Point { x: 0, y: 5 };
+    /// let max = Point { x: 15, y: 25 };
+    /// let clamped_p = p.clamp(&min, &max);
+    /// assert_eq!(clamped_p, Point { x: 10, y: 20 });
+    ///
+    /// let p_out_of_bounds = Point { x: -5, y: 30 };
+    /// let clamped_p_out_of_bounds = p_out_of_bounds.clamp(&min, &max);
+    /// assert_eq!(clamped_p_out_of_bounds, Point { x: 0, y: 25 });
+    /// ```
+    pub fn clamp(&self, min: &Self, max: &Self) -> Self {
+        self.max(min).min(max)
+    }
+}
+
+impl<T: Clone + Default + Debug> Clone for Point<T> {
+    fn clone(&self) -> Self {
+        Self {
+            x: self.x.clone(),
+            y: self.y.clone(),
+        }
+    }
+}
+
+/// A structure representing a two-dimensional size with width and height in a given unit.
+///
+/// This struct is generic over the type `T`, which can be any type that implements `Clone`, `Default`, and `Debug`.
+/// It is commonly used to specify dimensions for elements in a UI, such as a window or element.
+#[derive(Refineable, Default, Clone, Copy, PartialEq, Div, Hash, Serialize, Deserialize)]
+#[refineable(Debug)]
+#[repr(C)]
+pub struct Size<T: Clone + Default + Debug> {
+    pub width: T,
+    pub height: T,
+}
+
+/// Constructs a new `Size<T>` with the provided width and height.
+///
+/// # Arguments
+///
+/// * `width` - The width component of the `Size`.
+/// * `height` - The height component of the `Size`.
+///
+/// # Examples
+///
+/// ```
+/// # use zed::Size;
+/// let my_size = size(10, 20);
+/// assert_eq!(my_size.width, 10);
+/// assert_eq!(my_size.height, 20);
+/// ```
+pub fn size<T>(width: T, height: T) -> Size<T>
+where
+    T: Clone + Default + Debug,
+{
+    Size { width, height }
+}
+
+impl<T> Size<T>
+where
+    T: Clone + Default + Debug,
+{
+    /// Applies a function to the width and height of the size, producing a new `Size<U>`.
+    ///
+    /// This method allows for converting a `Size<T>` to a `Size<U>` by specifying a closure
+    /// that defines how to convert between the two types. The closure is applied to both the `width`
+    /// and `height`, resulting in a new size of the desired type.
+    ///
+    /// # Arguments
+    ///
+    /// * `f` - A closure that takes a value of type `T` and returns a value of type `U`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::Size;
+    /// let my_size = Size { width: 10, height: 20 };
+    /// let my_new_size = my_size.map(|dimension| dimension as f32 * 1.5);
+    /// assert_eq!(my_new_size, Size { width: 15.0, height: 30.0 });
+    /// ```
+    pub fn map<U>(&self, f: impl Fn(T) -> U) -> Size<U>
+    where
+        U: Clone + Default + Debug,
+    {
+        Size {
+            width: f(self.width.clone()),
+            height: f(self.height.clone()),
+        }
+    }
+}
+
+impl Size<Pixels> {
+    /// Scales the size by a given factor.
+    ///
+    /// This method multiplies both the width and height by the provided scaling factor,
+    /// resulting in a new `Size<ScaledPixels>` that is proportionally larger or smaller
+    /// depending on the factor.
+    ///
+    /// # Arguments
+    ///
+    /// * `factor` - The scaling factor to apply to the width and height.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::{Size, Pixels, ScaledPixels};
+    /// let size = Size { width: Pixels(100.0), height: Pixels(50.0) };
+    /// let scaled_size = size.scale(2.0);
+    /// assert_eq!(scaled_size, Size { width: ScaledPixels(200.0), height: ScaledPixels(100.0) });
+    /// ```
+    pub fn scale(&self, factor: f32) -> Size<ScaledPixels> {
+        Size {
+            width: self.width.scale(factor),
+            height: self.height.scale(factor),
+        }
+    }
+}
+
+impl<T> Along for Size<T>
+where
+    T: Clone + Default + Debug,
+{
+    type Unit = T;
+
+    fn along(&self, axis: Axis) -> T {
+        match axis {
+            Axis::Horizontal => self.width.clone(),
+            Axis::Vertical => self.height.clone(),
+        }
+    }
+
+    /// Returns the value of this size along the given axis.
+    fn apply_along(&self, axis: Axis, f: impl FnOnce(T) -> T) -> Self {
+        match axis {
+            Axis::Horizontal => Size {
+                width: f(self.width.clone()),
+                height: self.height.clone(),
+            },
+            Axis::Vertical => Size {
+                width: self.width.clone(),
+                height: f(self.height.clone()),
+            },
+        }
+    }
+}
+
+impl<T> Size<T>
+where
+    T: PartialOrd + Clone + Default + Debug,
+{
+    /// Returns a new `Size` with the maximum width and height from `self` and `other`.
+    ///
+    /// # Arguments
+    ///
+    /// * `other` - A reference to another `Size` to compare with `self`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::Size;
+    /// let size1 = Size { width: 30, height: 40 };
+    /// let size2 = Size { width: 50, height: 20 };
+    /// let max_size = size1.max(&size2);
+    /// assert_eq!(max_size, Size { width: 50, height: 40 });
+    /// ```
+    pub fn max(&self, other: &Self) -> Self {
+        Size {
+            width: if self.width >= other.width {
+                self.width.clone()
+            } else {
+                other.width.clone()
+            },
+            height: if self.height >= other.height {
+                self.height.clone()
+            } else {
+                other.height.clone()
+            },
+        }
+    }
+}
+
+impl<T> Sub for Size<T>
+where
+    T: Sub<Output = T> + Clone + Default + Debug,
+{
+    type Output = Size<T>;
+
+    fn sub(self, rhs: Self) -> Self::Output {
+        Size {
+            width: self.width - rhs.width,
+            height: self.height - rhs.height,
+        }
+    }
+}
+
+impl<T, Rhs> Mul<Rhs> for Size<T>
+where
+    T: Mul<Rhs, Output = Rhs> + Clone + Default + Debug,
+    Rhs: Clone + Default + Debug,
+{
+    type Output = Size<Rhs>;
+
+    fn mul(self, rhs: Rhs) -> Self::Output {
+        Size {
+            width: self.width * rhs.clone(),
+            height: self.height * rhs,
+        }
+    }
+}
+
+impl<T, S> MulAssign<S> for Size<T>
+where
+    T: Mul<S, Output = T> + Clone + Default + Debug,
+    S: Clone,
+{
+    fn mul_assign(&mut self, rhs: S) {
+        self.width = self.width.clone() * rhs.clone();
+        self.height = self.height.clone() * rhs;
+    }
+}
+
+impl<T> Eq for Size<T> where T: Eq + Default + Debug + Clone {}
+
+impl<T> Debug for Size<T>
+where
+    T: Clone + Default + Debug,
+{
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "Size {{ {:?} × {:?} }}", self.width, self.height)
+    }
+}
+
+impl<T: Clone + Default + Debug> From<Point<T>> for Size<T> {
+    fn from(point: Point<T>) -> Self {
+        Self {
+            width: point.x,
+            height: point.y,
+        }
+    }
+}
+
+impl From<Size<Pixels>> for Size<GlobalPixels> {
+    fn from(size: Size<Pixels>) -> Self {
+        Size {
+            width: GlobalPixels(size.width.0),
+            height: GlobalPixels(size.height.0),
+        }
+    }
+}
+
+impl From<Size<Pixels>> for Size<DefiniteLength> {
+    fn from(size: Size<Pixels>) -> Self {
+        Size {
+            width: size.width.into(),
+            height: size.height.into(),
+        }
+    }
+}
+
+impl From<Size<Pixels>> for Size<AbsoluteLength> {
+    fn from(size: Size<Pixels>) -> Self {
+        Size {
+            width: size.width.into(),
+            height: size.height.into(),
+        }
+    }
+}
+
+impl Size<Length> {
+    /// Returns a `Size` with both width and height set to fill the available space.
+    ///
+    /// This function creates a `Size` instance where both the width and height are set to `Length::Definite(DefiniteLength::Fraction(1.0))`,
+    /// which represents 100% of the available space in both dimensions.
+    ///
+    /// # Returns
+    ///
+    /// A `Size<Length>` that will fill the available space when used in a layout.
+    pub fn full() -> Self {
+        Self {
+            width: relative(1.).into(),
+            height: relative(1.).into(),
+        }
+    }
+}
+
+impl Size<Length> {
+    /// Returns a `Size` with both width and height set to `auto`, which allows the layout engine to determine the size.
+    ///
+    /// This function creates a `Size` instance where both the width and height are set to `Length::Auto`,
+    /// indicating that their size should be computed based on the layout context, such as the content size or
+    /// available space.
+    ///
+    /// # Returns
+    ///
+    /// A `Size<Length>` with width and height set to `Length::Auto`.
+    pub fn auto() -> Self {
+        Self {
+            width: Length::Auto,
+            height: Length::Auto,
+        }
+    }
+}
+
+/// Represents a rectangular area in a 2D space with an origin point and a size.
+///
+/// The `Bounds` struct is generic over a type `T` which represents the type of the coordinate system.
+/// The origin is represented as a `Point<T>` which defines the upper-left corner of the rectangle,
+/// and the size is represented as a `Size<T>` which defines the width and height of the rectangle.
+///
+/// # Examples
+///
+/// ```
+/// # use zed::{Bounds, Point, Size};
+/// let origin = Point { x: 0, y: 0 };
+/// let size = Size { width: 10, height: 20 };
+/// let bounds = Bounds::new(origin, size);
+///
+/// assert_eq!(bounds.origin, origin);
+/// assert_eq!(bounds.size, size);
+/// ```
+#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)]
+#[refineable(Debug)]
+#[repr(C)]
+pub struct Bounds<T: Clone + Default + Debug> {
+    pub origin: Point<T>,
+    pub size: Size<T>,
+}
+
+impl<T> Bounds<T>
+where
+    T: Clone + Debug + Sub<Output = T> + Default,
+{
+    /// Constructs a `Bounds` from two corner points: the upper-left and lower-right corners.
+    ///
+    /// This function calculates the origin and size of the `Bounds` based on the provided corner points.
+    /// The origin is set to the upper-left corner, and the size is determined by the difference between
+    /// the x and y coordinates of the lower-right and upper-left points.
+    ///
+    /// # Arguments
+    ///
+    /// * `upper_left` - A `Point<T>` representing the upper-left corner of the rectangle.
+    /// * `lower_right` - A `Point<T>` representing the lower-right corner of the rectangle.
+    ///
+    /// # Returns
+    ///
+    /// Returns a `Bounds<T>` that encompasses the area defined by the two corner points.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::{Bounds, Point};
+    /// let upper_left = Point { x: 0, y: 0 };
+    /// let lower_right = Point { x: 10, y: 10 };
+    /// let bounds = Bounds::from_corners(upper_left, lower_right);
+    ///
+    /// assert_eq!(bounds.origin, upper_left);
+    /// assert_eq!(bounds.size.width, 10);
+    /// assert_eq!(bounds.size.height, 10);
+    /// ```
+    pub fn from_corners(upper_left: Point<T>, lower_right: Point<T>) -> Self {
+        let origin = Point {
+            x: upper_left.x.clone(),
+            y: upper_left.y.clone(),
+        };
+        let size = Size {
+            width: lower_right.x - upper_left.x,
+            height: lower_right.y - upper_left.y,
+        };
+        Bounds { origin, size }
+    }
+
+    /// Creates a new `Bounds` with the specified origin and size.
+    ///
+    /// # Arguments
+    ///
+    /// * `origin` - A `Point<T>` representing the origin of the bounds.
+    /// * `size` - A `Size<T>` representing the size of the bounds.
+    ///
+    /// # Returns
+    ///
+    /// Returns a `Bounds<T>` that has the given origin and size.
+    pub fn new(origin: Point<T>, size: Size<T>) -> Self {
+        Bounds { origin, size }
+    }
+}
+
+impl<T> Bounds<T>
+where
+    T: Clone + Debug + PartialOrd + Add<T, Output = T> + Sub<Output = T> + Default + Half,
+{
+    /// Checks if this `Bounds` intersects with another `Bounds`.
+    ///
+    /// Two `Bounds` instances intersect if they overlap in the 2D space they occupy.
+    /// This method checks if there is any overlapping area between the two bounds.
+    ///
+    /// # Arguments
+    ///
+    /// * `other` - A reference to another `Bounds` to check for intersection with.
+    ///
+    /// # Returns
+    ///
+    /// Returns `true` if there is any intersection between the two bounds, `false` otherwise.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::{Bounds, Point, Size};
+    /// let bounds1 = Bounds {
+    ///     origin: Point { x: 0, y: 0 },
+    ///     size: Size { width: 10, height: 10 },
+    /// };
+    /// let bounds2 = Bounds {
+    ///     origin: Point { x: 5, y: 5 },
+    ///     size: Size { width: 10, height: 10 },
+    /// };
+    /// let bounds3 = Bounds {
+    ///     origin: Point { x: 20, y: 20 },
+    ///     size: Size { width: 10, height: 10 },
+    /// };
+    ///
+    /// assert_eq!(bounds1.intersects(&bounds2), true); // Overlapping bounds
+    /// assert_eq!(bounds1.intersects(&bounds3), false); // Non-overlapping bounds
+    /// ```
+    pub fn intersects(&self, other: &Bounds<T>) -> bool {
+        let my_lower_right = self.lower_right();
+        let their_lower_right = other.lower_right();
+
+        self.origin.x < their_lower_right.x
+            && my_lower_right.x > other.origin.x
+            && self.origin.y < their_lower_right.y
+            && my_lower_right.y > other.origin.y
+    }
+
+    /// Dilates the bounds by a specified amount in all directions.
+    ///
+    /// This method expands the bounds by the given `amount`, increasing the size
+    /// and adjusting the origin so that the bounds grow outwards equally in all directions.
+    /// The resulting bounds will have its width and height increased by twice the `amount`
+    /// (since it grows in both directions), and the origin will be moved by `-amount`
+    /// in both the x and y directions.
+    ///
+    /// # Arguments
+    ///
+    /// * `amount` - The amount by which to dilate the bounds.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::{Bounds, Point, Size};
+    /// let mut bounds = Bounds {
+    ///     origin: Point { x: 10, y: 10 },
+    ///     size: Size { width: 10, height: 10 },
+    /// };
+    /// bounds.dilate(5);
+    /// assert_eq!(bounds, Bounds {
+    ///     origin: Point { x: 5, y: 5 },
+    ///     size: Size { width: 20, height: 20 },
+    /// });
+    /// ```
+    pub fn dilate(&mut self, amount: T) {
+        self.origin.x = self.origin.x.clone() - amount.clone();
+        self.origin.y = self.origin.y.clone() - amount.clone();
+        let double_amount = amount.clone() + amount;
+        self.size.width = self.size.width.clone() + double_amount.clone();
+        self.size.height = self.size.height.clone() + double_amount;
+    }
+
+    /// Returns the center point of the bounds.
+    ///
+    /// Calculates the center by taking the origin's x and y coordinates and adding half the width and height
+    /// of the bounds, respectively. The center is represented as a `Point<T>` where `T` is the type of the
+    /// coordinate system.
+    ///
+    /// # Returns
+    ///
+    /// A `Point<T>` representing the center of the bounds.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::{Bounds, Point, Size};
+    /// let bounds = Bounds {
+    ///     origin: Point { x: 0, y: 0 },
+    ///     size: Size { width: 10, height: 20 },
+    /// };
+    /// let center = bounds.center();
+    /// assert_eq!(center, Point { x: 5, y: 10 });
+    /// ```
+    pub fn center(&self) -> Point<T> {
+        Point {
+            x: self.origin.x.clone() + self.size.width.clone().half(),
+            y: self.origin.y.clone() + self.size.height.clone().half(),
+        }
+    }
+}
+
+impl<T: Clone + Default + Debug + PartialOrd + Add<T, Output = T> + Sub<Output = T>> Bounds<T> {
+    /// Calculates the intersection of two `Bounds` objects.
+    ///
+    /// This method computes the overlapping region of two `Bounds`. If the bounds do not intersect,
+    /// the resulting `Bounds` will have a size with width and height of zero.
+    ///
+    /// # Arguments
+    ///
+    /// * `other` - A reference to another `Bounds` to intersect with.
+    ///
+    /// # Returns
+    ///
+    /// Returns a `Bounds` representing the intersection area. If there is no intersection,
+    /// the returned `Bounds` will have a size with width and height of zero.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::{Bounds, Point, Size};
+    /// let bounds1 = Bounds {
+    ///     origin: Point { x: 0, y: 0 },
+    ///     size: Size { width: 10, height: 10 },
+    /// };
+    /// let bounds2 = Bounds {
+    ///     origin: Point { x: 5, y: 5 },
+    ///     size: Size { width: 10, height: 10 },
+    /// };
+    /// let intersection = bounds1.intersect(&bounds2);
+    ///
+    /// assert_eq!(intersection, Bounds {
+    ///     origin: Point { x: 5, y: 5 },
+    ///     size: Size { width: 5, height: 5 },
+    /// });
+    /// ```
+    pub fn intersect(&self, other: &Self) -> Self {
+        let upper_left = self.origin.max(&other.origin);
+        let lower_right = self.lower_right().min(&other.lower_right());
+        Self::from_corners(upper_left, lower_right)
+    }
+
+    /// Computes the union of two `Bounds`.
+    ///
+    /// This method calculates the smallest `Bounds` that contains both the current `Bounds` and the `other` `Bounds`.
+    /// The resulting `Bounds` will have an origin that is the minimum of the origins of the two `Bounds`,
+    /// and a size that encompasses the furthest extents of both `Bounds`.
+    ///
+    /// # Arguments
+    ///
+    /// * `other` - A reference to another `Bounds` to create a union with.
+    ///
+    /// # Returns
+    ///
+    /// Returns a `Bounds` representing the union of the two `Bounds`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::{Bounds, Point, Size};
+    /// let bounds1 = Bounds {
+    ///     origin: Point { x: 0, y: 0 },
+    ///     size: Size { width: 10, height: 10 },
+    /// };
+    /// let bounds2 = Bounds {
+    ///     origin: Point { x: 5, y: 5 },
+    ///     size: Size { width: 15, height: 15 },
+    /// };
+    /// let union_bounds = bounds1.union(&bounds2);
+    ///
+    /// assert_eq!(union_bounds, Bounds {
+    ///     origin: Point { x: 0, y: 0 },
+    ///     size: Size { width: 20, height: 20 },
+    /// });
+    /// ```
+    pub fn union(&self, other: &Self) -> Self {
+        let top_left = self.origin.min(&other.origin);
+        let bottom_right = self.lower_right().max(&other.lower_right());
+        Bounds::from_corners(top_left, bottom_right)
+    }
+}
+
+impl<T, Rhs> Mul<Rhs> for Bounds<T>
+where
+    T: Mul<Rhs, Output = Rhs> + Clone + Default + Debug,
+    Point<T>: Mul<Rhs, Output = Point<Rhs>>,
+    Rhs: Clone + Default + Debug,
+{
+    type Output = Bounds<Rhs>;
+
+    fn mul(self, rhs: Rhs) -> Self::Output {
+        Bounds {
+            origin: self.origin * rhs.clone(),
+            size: self.size * rhs,
+        }
+    }
+}
+
+impl<T, S> MulAssign<S> for Bounds<T>
+where
+    T: Mul<S, Output = T> + Clone + Default + Debug,
+    S: Clone,
+{
+    fn mul_assign(&mut self, rhs: S) {
+        self.origin *= rhs.clone();
+        self.size *= rhs;
+    }
+}
+
+impl<T, S> Div<S> for Bounds<T>
+where
+    Size<T>: Div<S, Output = Size<T>>,
+    T: Div<S, Output = T> + Default + Clone + Debug,
+    S: Clone,
+{
+    type Output = Self;
+
+    fn div(self, rhs: S) -> Self {
+        Self {
+            origin: self.origin / rhs.clone(),
+            size: self.size / rhs,
+        }
+    }
+}
+
+impl<T> Bounds<T>
+where
+    T: Add<T, Output = T> + Clone + Default + Debug,
+{
+    /// Returns the top edge of the bounds.
+    ///
+    /// # Returns
+    ///
+    /// A value of type `T` representing the y-coordinate of the top edge of the bounds.
+    pub fn top(&self) -> T {
+        self.origin.y.clone()
+    }
+
+    /// Returns the bottom edge of the bounds.
+    ///
+    /// # Returns
+    ///
+    /// A value of type `T` representing the y-coordinate of the bottom edge of the bounds.
+    pub fn bottom(&self) -> T {
+        self.origin.y.clone() + self.size.height.clone()
+    }
+
+    /// Returns the left edge of the bounds.
+    ///
+    /// # Returns
+    ///
+    /// A value of type `T` representing the x-coordinate of the left edge of the bounds.
+    pub fn left(&self) -> T {
+        self.origin.x.clone()
+    }
+
+    /// Returns the right edge of the bounds.
+    ///
+    /// # Returns
+    ///
+    /// A value of type `T` representing the x-coordinate of the right edge of the bounds.
+    pub fn right(&self) -> T {
+        self.origin.x.clone() + self.size.width.clone()
+    }
+
+    /// Returns the upper-right corner point of the bounds.
+    ///
+    /// # Returns
+    ///
+    /// A `Point<T>` representing the upper-right corner of the bounds.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::{Bounds, Point, Size};
+    /// let bounds = Bounds {
+    ///     origin: Point { x: 0, y: 0 },
+    ///     size: Size { width: 10, height: 20 },
+    /// };
+    /// let upper_right = bounds.upper_right();
+    /// assert_eq!(upper_right, Point { x: 10, y: 0 });
+    /// ```
+    pub fn upper_right(&self) -> Point<T> {
+        Point {
+            x: self.origin.x.clone() + self.size.width.clone(),
+            y: self.origin.y.clone(),
+        }
+    }
+
+    /// Returns the lower-right corner point of the bounds.
+    ///
+    /// # Returns
+    ///
+    /// A `Point<T>` representing the lower-right corner of the bounds.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::{Bounds, Point, Size};
+    /// let bounds = Bounds {
+    ///     origin: Point { x: 0, y: 0 },
+    ///     size: Size { width: 10, height: 20 },
+    /// };
+    /// let lower_right = bounds.lower_right();
+    /// assert_eq!(lower_right, Point { x: 10, y: 20 });
+    /// ```
+    pub fn lower_right(&self) -> Point<T> {
+        Point {
+            x: self.origin.x.clone() + self.size.width.clone(),
+            y: self.origin.y.clone() + self.size.height.clone(),
+        }
+    }
+
+    /// Returns the lower-left corner point of the bounds.
+    ///
+    /// # Returns
+    ///
+    /// A `Point<T>` representing the lower-left corner of the bounds.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::{Bounds, Point, Size};
+    /// let bounds = Bounds {
+    ///     origin: Point { x: 0, y: 0 },
+    ///     size: Size { width: 10, height: 20 },
+    /// };
+    /// let lower_left = bounds.lower_left();
+    /// assert_eq!(lower_left, Point { x: 0, y: 20 });
+    /// ```
+    pub fn lower_left(&self) -> Point<T> {
+        Point {
+            x: self.origin.x.clone(),
+            y: self.origin.y.clone() + self.size.height.clone(),
+        }
+    }
+}
+
+impl<T> Bounds<T>
+where
+    T: Add<T, Output = T> + PartialOrd + Clone + Default + Debug,
+{
+    /// Checks if the given point is within the bounds.
+    ///
+    /// This method determines whether a point lies inside the rectangle defined by the bounds,
+    /// including the edges. The point is considered inside if its x-coordinate is greater than
+    /// or equal to the left edge and less than or equal to the right edge, and its y-coordinate
+    /// is greater than or equal to the top edge and less than or equal to the bottom edge of the bounds.
+    ///
+    /// # Arguments
+    ///
+    /// * `point` - A reference to a `Point<T>` that represents the point to check.
+    ///
+    /// # Returns
+    ///
+    /// Returns `true` if the point is within the bounds, `false` otherwise.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::{Point, Bounds};
+    /// let bounds = Bounds {
+    ///     origin: Point { x: 0, y: 0 },
+    ///     size: Size { width: 10, height: 10 },
+    /// };
+    /// let inside_point = Point { x: 5, y: 5 };
+    /// let outside_point = Point { x: 15, y: 15 };
+    ///
+    /// assert!(bounds.contains_point(&inside_point));
+    /// assert!(!bounds.contains_point(&outside_point));
+    /// ```
+    pub fn contains(&self, point: &Point<T>) -> bool {
+        point.x >= self.origin.x
+            && point.x <= self.origin.x.clone() + self.size.width.clone()
+            && point.y >= self.origin.y
+            && point.y <= self.origin.y.clone() + self.size.height.clone()
+    }
+
+    /// Applies a function to the origin and size of the bounds, producing a new `Bounds<U>`.
+    ///
+    /// This method allows for converting a `Bounds<T>` to a `Bounds<U>` by specifying a closure
+    /// that defines how to convert between the two types. The closure is applied to the `origin` and
+    /// `size` fields, resulting in new bounds of the desired type.
+    ///
+    /// # Arguments
+    ///
+    /// * `f` - A closure that takes a value of type `T` and returns a value of type `U`.
+    ///
+    /// # Returns
+    ///
+    /// Returns a new `Bounds<U>` with the origin and size mapped by the provided function.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::{Bounds, Point, Size};
+    /// let bounds = Bounds {
+    ///     origin: Point { x: 10.0, y: 10.0 },
+    ///     size: Size { width: 10.0, height: 20.0 },
+    /// };
+    /// let new_bounds = bounds.map(|value| value as f64 * 1.5);
+    ///
+    /// assert_eq!(new_bounds, Bounds {
+    ///     origin: Point { x: 15.0, y: 15.0 },
+    ///     size: Size { width: 15.0, height: 30.0 },
+    /// });
+    pub fn map<U>(&self, f: impl Fn(T) -> U) -> Bounds<U>
+    where
+        U: Clone + Default + Debug,
+    {
+        Bounds {
+            origin: self.origin.map(&f),
+            size: self.size.map(f),
+        }
+    }
+}
+
+impl Bounds<Pixels> {
+    /// Scales the bounds by a given factor, typically used to adjust for display scaling.
+    ///
+    /// This method multiplies the origin and size of the bounds by the provided scaling factor,
+    /// resulting in a new `Bounds<ScaledPixels>` that is proportionally larger or smaller
+    /// depending on the scaling factor. This can be used to ensure that the bounds are properly
+    /// scaled for different display densities.
+    ///
+    /// # Arguments
+    ///
+    /// * `factor` - The scaling factor to apply to the origin and size, typically the display's scaling factor.
+    ///
+    /// # Returns
+    ///
+    /// Returns a new `Bounds<ScaledPixels>` that represents the scaled bounds.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::{Bounds, Point, Size, Pixels};
+    /// let bounds = Bounds {
+    ///     origin: Point { x: Pixels(10.0), y: Pixels(20.0) },
+    ///     size: Size { width: Pixels(30.0), height: Pixels(40.0) },
+    /// };
+    /// let display_scale_factor = 2.0;
+    /// let scaled_bounds = bounds.scale(display_scale_factor);
+    /// assert_eq!(scaled_bounds, Bounds {
+    ///     origin: Point { x: ScaledPixels(20.0), y: ScaledPixels(40.0) },
+    ///     size: Size { width: ScaledPixels(60.0), height: ScaledPixels(80.0) },
+    /// });
+    /// ```
+    pub fn scale(&self, factor: f32) -> Bounds<ScaledPixels> {
+        Bounds {
+            origin: self.origin.scale(factor),
+            size: self.size.scale(factor),
+        }
+    }
+}
+
+impl<T: Clone + Debug + Copy + Default> Copy for Bounds<T> {}
+
+/// Represents the edges of a box in a 2D space, such as padding or margin.
+///
+/// Each field represents the size of the edge on one side of the box: `top`, `right`, `bottom`, and `left`.
+///
+/// # Examples
+///
+/// ```
+/// # use zed::Edges;
+/// let edges = Edges {
+///     top: 10.0,
+///     right: 20.0,
+///     bottom: 30.0,
+///     left: 40.0,
+/// };
+///
+/// assert_eq!(edges.top, 10.0);
+/// assert_eq!(edges.right, 20.0);
+/// assert_eq!(edges.bottom, 30.0);
+/// assert_eq!(edges.left, 40.0);
+/// ```
+#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)]
+#[refineable(Debug)]
+#[repr(C)]
+pub struct Edges<T: Clone + Default + Debug> {
+    pub top: T,
+    pub right: T,
+    pub bottom: T,
+    pub left: T,
+}
+
+impl<T> Mul for Edges<T>
+where
+    T: Mul<Output = T> + Clone + Default + Debug,
+{
+    type Output = Self;
+
+    fn mul(self, rhs: Self) -> Self::Output {
+        Self {
+            top: self.top.clone() * rhs.top,
+            right: self.right.clone() * rhs.right,
+            bottom: self.bottom.clone() * rhs.bottom,
+            left: self.left.clone() * rhs.left,
+        }
+    }
+}
+
+impl<T, S> MulAssign<S> for Edges<T>
+where
+    T: Mul<S, Output = T> + Clone + Default + Debug,
+    S: Clone,
+{
+    fn mul_assign(&mut self, rhs: S) {
+        self.top = self.top.clone() * rhs.clone();
+        self.right = self.right.clone() * rhs.clone();
+        self.bottom = self.bottom.clone() * rhs.clone();
+        self.left = self.left.clone() * rhs;
+    }
+}
+
+impl<T: Clone + Default + Debug + Copy> Copy for Edges<T> {}
+
+impl<T: Clone + Default + Debug> Edges<T> {
+    /// Constructs `Edges` where all sides are set to the same specified value.
+    ///
+    /// This function creates an `Edges` instance with the `top`, `right`, `bottom`, and `left` fields all initialized
+    /// to the same value provided as an argument. This is useful when you want to have uniform edges around a box,
+    /// such as padding or margin with the same size on all sides.
+    ///
+    /// # Arguments
+    ///
+    /// * `value` - The value to set for all four sides of the edges.
+    ///
+    /// # Returns
+    ///
+    /// An `Edges` instance with all sides set to the given value.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::Edges;
+    /// let uniform_edges = Edges::all(10.0);
+    /// assert_eq!(uniform_edges.top, 10.0);
+    /// assert_eq!(uniform_edges.right, 10.0);
+    /// assert_eq!(uniform_edges.bottom, 10.0);
+    /// assert_eq!(uniform_edges.left, 10.0);
+    /// ```
+    pub fn all(value: T) -> Self {
+        Self {
+            top: value.clone(),
+            right: value.clone(),
+            bottom: value.clone(),
+            left: value,
+        }
+    }
+
+    /// Applies a function to each field of the `Edges`, producing a new `Edges<U>`.
+    ///
+    /// This method allows for converting an `Edges<T>` to an `Edges<U>` by specifying a closure
+    /// that defines how to convert between the two types. The closure is applied to each field
+    /// (`top`, `right`, `bottom`, `left`), resulting in new edges of the desired type.
+    ///
+    /// # Arguments
+    ///
+    /// * `f` - A closure that takes a reference to a value of type `T` and returns a value of type `U`.
+    ///
+    /// # Returns
+    ///
+    /// Returns a new `Edges<U>` with each field mapped by the provided function.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::Edges;
+    /// let edges = Edges { top: 10, right: 20, bottom: 30, left: 40 };
+    /// let edges_float = edges.map(|&value| value as f32 * 1.1);
+    /// assert_eq!(edges_float, Edges { top: 11.0, right: 22.0, bottom: 33.0, left: 44.0 });
+    /// ```
+    pub fn map<U>(&self, f: impl Fn(&T) -> U) -> Edges<U>
+    where
+        U: Clone + Default + Debug,
+    {
+        Edges {
+            top: f(&self.top),
+            right: f(&self.right),
+            bottom: f(&self.bottom),
+            left: f(&self.left),
+        }
+    }
+
+    /// Checks if any of the edges satisfy a given predicate.
+    ///
+    /// This method applies a predicate function to each field of the `Edges` and returns `true` if any field satisfies the predicate.
+    ///
+    /// # Arguments
+    ///
+    /// * `predicate` - A closure that takes a reference to a value of type `T` and returns a `bool`.
+    ///
+    /// # Returns
+    ///
+    /// Returns `true` if the predicate returns `true` for any of the edge values, `false` otherwise.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::Edges;
+    /// let edges = Edges {
+    ///     top: 10,
+    ///     right: 0,
+    ///     bottom: 5,
+    ///     left: 0,
+    /// };
+    ///
+    /// assert!(edges.any(|value| *value == 0));
+    /// assert!(edges.any(|value| *value > 0));
+    /// assert!(!edges.any(|value| *value > 10));
+    /// ```
+    pub fn any<F: Fn(&T) -> bool>(&self, predicate: F) -> bool {
+        predicate(&self.top)
+            || predicate(&self.right)
+            || predicate(&self.bottom)
+            || predicate(&self.left)
+    }
+}
+
+impl Edges<Length> {
+    /// Sets the edges of the `Edges` struct to `auto`, which is a special value that allows the layout engine to automatically determine the size of the edges.
+    ///
+    /// This is typically used in layout contexts where the exact size of the edges is not important, or when the size should be calculated based on the content or container.
+    ///
+    /// # Returns
+    ///
+    /// Returns an `Edges<Length>` with all edges set to `Length::Auto`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::Edges;
+    /// let auto_edges = Edges::auto();
+    /// assert_eq!(auto_edges.top, Length::Auto);
+    /// assert_eq!(auto_edges.right, Length::Auto);
+    /// assert_eq!(auto_edges.bottom, Length::Auto);
+    /// assert_eq!(auto_edges.left, Length::Auto);
+    /// ```
+    pub fn auto() -> Self {
+        Self {
+            top: Length::Auto,
+            right: Length::Auto,
+            bottom: Length::Auto,
+            left: Length::Auto,
+        }
+    }
+
+    /// Sets the edges of the `Edges` struct to zero, which means no size or thickness.
+    ///
+    /// This is typically used when you want to specify that a box (like a padding or margin area)
+    /// should have no edges, effectively making it non-existent or invisible in layout calculations.
+    ///
+    /// # Returns
+    ///
+    /// Returns an `Edges<Length>` with all edges set to zero length.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::Edges;
+    /// let no_edges = Edges::zero();
+    /// assert_eq!(no_edges.top, Length::Definite(DefiniteLength::from(Pixels(0.))));
+    /// assert_eq!(no_edges.right, Length::Definite(DefiniteLength::from(Pixels(0.))));
+    /// assert_eq!(no_edges.bottom, Length::Definite(DefiniteLength::from(Pixels(0.))));
+    /// assert_eq!(no_edges.left, Length::Definite(DefiniteLength::from(Pixels(0.))));
+    /// ```
+    pub fn zero() -> Self {
+        Self {
+            top: px(0.).into(),
+            right: px(0.).into(),
+            bottom: px(0.).into(),
+            left: px(0.).into(),
+        }
+    }
+}
+
+impl Edges<DefiniteLength> {
+    /// Sets the edges of the `Edges` struct to zero, which means no size or thickness.
+    ///
+    /// This is typically used when you want to specify that a box (like a padding or margin area)
+    /// should have no edges, effectively making it non-existent or invisible in layout calculations.
+    ///
+    /// # Returns
+    ///
+    /// Returns an `Edges<DefiniteLength>` with all edges set to zero length.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::Edges;
+    /// let no_edges = Edges::zero();
+    /// assert_eq!(no_edges.top, DefiniteLength::from(zed::px(0.)));
+    /// assert_eq!(no_edges.right, DefiniteLength::from(zed::px(0.)));
+    /// assert_eq!(no_edges.bottom, DefiniteLength::from(zed::px(0.)));
+    /// assert_eq!(no_edges.left, DefiniteLength::from(zed::px(0.)));
+    /// ```
+    pub fn zero() -> Self {
+        Self {
+            top: px(0.).into(),
+            right: px(0.).into(),
+            bottom: px(0.).into(),
+            left: px(0.).into(),
+        }
+    }
+
+    /// Converts the `DefiniteLength` to `Pixels` based on the parent size and the REM size.
+    ///
+    /// This method allows for a `DefiniteLength` value to be converted into pixels, taking into account
+    /// the size of the parent element (for percentage-based lengths) and the size of a rem unit (for rem-based lengths).
+    ///
+    /// # Arguments
+    ///
+    /// * `parent_size` - `Size<AbsoluteLength>` representing the size of the parent element.
+    /// * `rem_size` - `Pixels` representing the size of one REM unit.
+    ///
+    /// # Returns
+    ///
+    /// Returns an `Edges<Pixels>` representing the edges with lengths converted to pixels.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::{Edges, DefiniteLength, px, AbsoluteLength, Size};
+    /// let edges = Edges {
+    ///     top: DefiniteLength::Absolute(AbsoluteLength::Pixels(px(10.0))),
+    ///     right: DefiniteLength::Fraction(0.5),
+    ///     bottom: DefiniteLength::Absolute(AbsoluteLength::Rems(rems(2.0))),
+    ///     left: DefiniteLength::Fraction(0.25),
+    /// };
+    /// let parent_size = Size {
+    ///     width: AbsoluteLength::Pixels(px(200.0)),
+    ///     height: AbsoluteLength::Pixels(px(100.0)),
+    /// };
+    /// let rem_size = px(16.0);
+    /// let edges_in_pixels = edges.to_pixels(parent_size, rem_size);
+    ///
+    /// assert_eq!(edges_in_pixels.top, px(10.0)); // Absolute length in pixels
+    /// assert_eq!(edges_in_pixels.right, px(100.0)); // 50% of parent width
+    /// assert_eq!(edges_in_pixels.bottom, px(32.0)); // 2 rems
+    /// assert_eq!(edges_in_pixels.left, px(50.0)); // 25% of parent width
+    /// ```
+    pub fn to_pixels(&self, parent_size: Size<AbsoluteLength>, rem_size: Pixels) -> Edges<Pixels> {
+        Edges {
+            top: self.top.to_pixels(parent_size.height, rem_size),
+            right: self.right.to_pixels(parent_size.width, rem_size),
+            bottom: self.bottom.to_pixels(parent_size.height, rem_size),
+            left: self.left.to_pixels(parent_size.width, rem_size),
+        }
+    }
+}
+
+impl Edges<AbsoluteLength> {
+    /// Sets the edges of the `Edges` struct to zero, which means no size or thickness.
+    ///
+    /// This is typically used when you want to specify that a box (like a padding or margin area)
+    /// should have no edges, effectively making it non-existent or invisible in layout calculations.
+    ///
+    /// # Returns
+    ///
+    /// Returns an `Edges<AbsoluteLength>` with all edges set to zero length.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::Edges;
+    /// let no_edges = Edges::zero();
+    /// assert_eq!(no_edges.top, AbsoluteLength::Pixels(Pixels(0.0)));
+    /// assert_eq!(no_edges.right, AbsoluteLength::Pixels(Pixels(0.0)));
+    /// assert_eq!(no_edges.bottom, AbsoluteLength::Pixels(Pixels(0.0)));
+    /// assert_eq!(no_edges.left, AbsoluteLength::Pixels(Pixels(0.0)));
+    /// ```
+    pub fn zero() -> Self {
+        Self {
+            top: px(0.).into(),
+            right: px(0.).into(),
+            bottom: px(0.).into(),
+            left: px(0.).into(),
+        }
+    }
+
+    /// Converts the `AbsoluteLength` to `Pixels` based on the `rem_size`.
+    ///
+    /// If the `AbsoluteLength` is already in pixels, it simply returns the corresponding `Pixels` value.
+    /// If the `AbsoluteLength` is in rems, it multiplies the number of rems by the `rem_size` to convert it to pixels.
+    ///
+    /// # Arguments
+    ///
+    /// * `rem_size` - The size of one rem unit in pixels.
+    ///
+    /// # Returns
+    ///
+    /// Returns an `Edges<Pixels>` representing the edges with lengths converted to pixels.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::{Edges, AbsoluteLength, Pixels, px};
+    /// let edges = Edges {
+    ///     top: AbsoluteLength::Pixels(px(10.0)),
+    ///     right: AbsoluteLength::Rems(rems(1.0)),
+    ///     bottom: AbsoluteLength::Pixels(px(20.0)),
+    ///     left: AbsoluteLength::Rems(rems(2.0)),
+    /// };
+    /// let rem_size = px(16.0);
+    /// let edges_in_pixels = edges.to_pixels(rem_size);
+    ///
+    /// assert_eq!(edges_in_pixels.top, px(10.0)); // Already in pixels
+    /// assert_eq!(edges_in_pixels.right, px(16.0)); // 1 rem converted to pixels
+    /// assert_eq!(edges_in_pixels.bottom, px(20.0)); // Already in pixels
+    /// assert_eq!(edges_in_pixels.left, px(32.0)); // 2 rems converted to pixels
+    /// ```
+    pub fn to_pixels(&self, rem_size: Pixels) -> Edges<Pixels> {
+        Edges {
+            top: self.top.to_pixels(rem_size),
+            right: self.right.to_pixels(rem_size),
+            bottom: self.bottom.to_pixels(rem_size),
+            left: self.left.to_pixels(rem_size),
+        }
+    }
+}
+
+impl Edges<Pixels> {
+    /// Scales the `Edges<Pixels>` by a given factor, returning `Edges<ScaledPixels>`.
+    ///
+    /// This method is typically used for adjusting the edge sizes for different display densities or scaling factors.
+    ///
+    /// # Arguments
+    ///
+    /// * `factor` - The scaling factor to apply to each edge.
+    ///
+    /// # Returns
+    ///
+    /// Returns a new `Edges<ScaledPixels>` where each edge is the result of scaling the original edge by the given factor.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::{Edges, Pixels};
+    /// let edges = Edges {
+    ///     top: Pixels(10.0),
+    ///     right: Pixels(20.0),
+    ///     bottom: Pixels(30.0),
+    ///     left: Pixels(40.0),
+    /// };
+    /// let scaled_edges = edges.scale(2.0);
+    /// assert_eq!(scaled_edges.top, ScaledPixels(20.0));
+    /// assert_eq!(scaled_edges.right, ScaledPixels(40.0));
+    /// assert_eq!(scaled_edges.bottom, ScaledPixels(60.0));
+    /// assert_eq!(scaled_edges.left, ScaledPixels(80.0));
+    /// ```
+    pub fn scale(&self, factor: f32) -> Edges<ScaledPixels> {
+        Edges {
+            top: self.top.scale(factor),
+            right: self.right.scale(factor),
+            bottom: self.bottom.scale(factor),
+            left: self.left.scale(factor),
+        }
+    }
+
+    /// Returns the maximum value of any edge.
+    ///
+    /// # Returns
+    ///
+    /// The maximum `Pixels` value among all four edges.
+    pub fn max(&self) -> Pixels {
+        self.top.max(self.right).max(self.bottom).max(self.left)
+    }
+}
+
+impl From<f32> for Edges<Pixels> {
+    fn from(val: f32) -> Self {
+        Edges {
+            top: val.into(),
+            right: val.into(),
+            bottom: val.into(),
+            left: val.into(),
+        }
+    }
+}
+
+/// Represents the corners of a box in a 2D space, such as border radius.
+///
+/// Each field represents the size of the corner on one side of the box: `top_left`, `top_right`, `bottom_right`, and `bottom_left`.
+/// ```
+#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)]
+#[refineable(Debug)]
+#[repr(C)]
+pub struct Corners<T: Clone + Default + Debug> {
+    pub top_left: T,
+    pub top_right: T,
+    pub bottom_right: T,
+    pub bottom_left: T,
+}
+
+impl<T> Corners<T>
+where
+    T: Clone + Default + Debug,
+{
+    /// Constructs `Corners` where all sides are set to the same specified value.
+    ///
+    /// This function creates a `Corners` instance with the `top_left`, `top_right`, `bottom_right`, and `bottom_left` fields all initialized
+    /// to the same value provided as an argument. This is useful when you want to have uniform corners around a box,
+    /// such as a uniform border radius on a rectangle.
+    ///
+    /// # Arguments
+    ///
+    /// * `value` - The value to set for all four corners.
+    ///
+    /// # Returns
+    ///
+    /// An `Corners` instance with all corners set to the given value.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::Corners;
+    /// let uniform_corners = Corners::all(5.0);
+    /// assert_eq!(uniform_corners.top_left, 5.0);
+    /// assert_eq!(uniform_corners.top_right, 5.0);
+    /// assert_eq!(uniform_corners.bottom_right, 5.0);
+    /// assert_eq!(uniform_corners.bottom_left, 5.0);
+    /// ```
+    pub fn all(value: T) -> Self {
+        Self {
+            top_left: value.clone(),
+            top_right: value.clone(),
+            bottom_right: value.clone(),
+            bottom_left: value,
+        }
+    }
+}
+
+impl Corners<AbsoluteLength> {
+    /// Converts the `AbsoluteLength` to `Pixels` based on the provided size and rem size, ensuring the resulting
+    /// `Pixels` do not exceed half of the maximum of the provided size's width and height.
+    ///
+    /// This method is particularly useful when dealing with corner radii, where the radius in pixels should not
+    /// exceed half the size of the box it applies to, to avoid the corners overlapping.
+    ///
+    /// # Arguments
+    ///
+    /// * `size` - The `Size<Pixels>` against which the maximum allowable radius is determined.
+    /// * `rem_size` - The size of one REM unit in pixels, used for conversion if the `AbsoluteLength` is in REMs.
+    ///
+    /// # Returns
+    ///
+    /// Returns a `Corners<Pixels>` instance with each corner's length converted to pixels and clamped to the
+    /// maximum allowable radius based on the provided size.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::{Corners, AbsoluteLength, Pixels, Size};
+    /// let corners = Corners {
+    ///     top_left: AbsoluteLength::Pixels(Pixels(15.0)),
+    ///     top_right: AbsoluteLength::Rems(Rems(1.0)),
+    ///     bottom_right: AbsoluteLength::Pixels(Pixels(20.0)),
+    ///     bottom_left: AbsoluteLength::Rems(Rems(2.0)),
+    /// };
+    /// let size = Size { width: Pixels(100.0), height: Pixels(50.0) };
+    /// let rem_size = Pixels(16.0);
+    /// let corners_in_pixels = corners.to_pixels(size, rem_size);
+    ///
+    /// // The resulting corners should not exceed half the size of the smallest dimension (50.0 / 2.0 = 25.0).
+    /// assert_eq!(corners_in_pixels.top_left, Pixels(15.0));
+    /// assert_eq!(corners_in_pixels.top_right, Pixels(16.0)); // 1 rem converted to pixels
+    /// assert_eq!(corners_in_pixels.bottom_right, Pixels(20.0).min(Pixels(25.0))); // Clamped to 25.0
+    /// assert_eq!(corners_in_pixels.bottom_left, Pixels(32.0).min(Pixels(25.0))); // 2 rems converted to pixels and clamped
+    /// ```
+    pub fn to_pixels(&self, size: Size<Pixels>, rem_size: Pixels) -> Corners<Pixels> {
+        let max = size.width.max(size.height) / 2.;
+        Corners {
+            top_left: self.top_left.to_pixels(rem_size).min(max),
+            top_right: self.top_right.to_pixels(rem_size).min(max),
+            bottom_right: self.bottom_right.to_pixels(rem_size).min(max),
+            bottom_left: self.bottom_left.to_pixels(rem_size).min(max),
+        }
+    }
+}
+
+impl Corners<Pixels> {
+    /// Scales the `Corners<Pixels>` by a given factor, returning `Corners<ScaledPixels>`.
+    ///
+    /// This method is typically used for adjusting the corner sizes for different display densities or scaling factors.
+    ///
+    /// # Arguments
+    ///
+    /// * `factor` - The scaling factor to apply to each corner.
+    ///
+    /// # Returns
+    ///
+    /// Returns a new `Corners<ScaledPixels>` where each corner is the result of scaling the original corner by the given factor.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::{Corners, Pixels};
+    /// let corners = Corners {
+    ///     top_left: Pixels(10.0),
+    ///     top_right: Pixels(20.0),
+    ///     bottom_right: Pixels(30.0),
+    ///     bottom_left: Pixels(40.0),
+    /// };
+    /// let scaled_corners = corners.scale(2.0);
+    /// assert_eq!(scaled_corners.top_left, ScaledPixels(20.0));
+    /// assert_eq!(scaled_corners.top_right, ScaledPixels(40.0));
+    /// assert_eq!(scaled_corners.bottom_right, ScaledPixels(60.0));
+    /// assert_eq!(scaled_corners.bottom_left, ScaledPixels(80.0));
+    /// ```
+    pub fn scale(&self, factor: f32) -> Corners<ScaledPixels> {
+        Corners {
+            top_left: self.top_left.scale(factor),
+            top_right: self.top_right.scale(factor),
+            bottom_right: self.bottom_right.scale(factor),
+            bottom_left: self.bottom_left.scale(factor),
+        }
+    }
+
+    /// Returns the maximum value of any corner.
+    ///
+    /// # Returns
+    ///
+    /// The maximum `Pixels` value among all four corners.
+    pub fn max(&self) -> Pixels {
+        self.top_left
+            .max(self.top_right)
+            .max(self.bottom_right)
+            .max(self.bottom_left)
+    }
+}
+
+impl<T: Clone + Default + Debug> Corners<T> {
+    /// Applies a function to each field of the `Corners`, producing a new `Corners<U>`.
+    ///
+    /// This method allows for converting a `Corners<T>` to a `Corners<U>` by specifying a closure
+    /// that defines how to convert between the two types. The closure is applied to each field
+    /// (`top_left`, `top_right`, `bottom_right`, `bottom_left`), resulting in new corners of the desired type.
+    ///
+    /// # Arguments
+    ///
+    /// * `f` - A closure that takes a reference to a value of type `T` and returns a value of type `U`.
+    ///
+    /// # Returns
+    ///
+    /// Returns a new `Corners<U>` with each field mapped by the provided function.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::{Corners, Pixels};
+    /// let corners = Corners {
+    ///     top_left: Pixels(10.0),
+    ///     top_right: Pixels(20.0),
+    ///     bottom_right: Pixels(30.0),
+    ///     bottom_left: Pixels(40.0),
+    /// };
+    /// let corners_in_rems = corners.map(|&px| Rems(px.0 / 16.0));
+    /// assert_eq!(corners_in_rems, Corners {
+    ///     top_left: Rems(0.625),
+    ///     top_right: Rems(1.25),
+    ///     bottom_right: Rems(1.875),
+    ///     bottom_left: Rems(2.5),
+    /// });
+    /// ```
+    pub fn map<U>(&self, f: impl Fn(&T) -> U) -> Corners<U>
+    where
+        U: Clone + Default + Debug,
+    {
+        Corners {
+            top_left: f(&self.top_left),
+            top_right: f(&self.top_right),
+            bottom_right: f(&self.bottom_right),
+            bottom_left: f(&self.bottom_left),
+        }
+    }
+}
+
+impl<T> Mul for Corners<T>
+where
+    T: Mul<Output = T> + Clone + Default + Debug,
+{
+    type Output = Self;
+
+    fn mul(self, rhs: Self) -> Self::Output {
+        Self {
+            top_left: self.top_left.clone() * rhs.top_left,
+            top_right: self.top_right.clone() * rhs.top_right,
+            bottom_right: self.bottom_right.clone() * rhs.bottom_right,
+            bottom_left: self.bottom_left.clone() * rhs.bottom_left,
+        }
+    }
+}
+
+impl<T, S> MulAssign<S> for Corners<T>
+where
+    T: Mul<S, Output = T> + Clone + Default + Debug,
+    S: Clone,
+{
+    fn mul_assign(&mut self, rhs: S) {
+        self.top_left = self.top_left.clone() * rhs.clone();
+        self.top_right = self.top_right.clone() * rhs.clone();
+        self.bottom_right = self.bottom_right.clone() * rhs.clone();
+        self.bottom_left = self.bottom_left.clone() * rhs;
+    }
+}
+
+impl<T> Copy for Corners<T> where T: Copy + Clone + Default + Debug {}
+
+impl From<f32> for Corners<Pixels> {
+    fn from(val: f32) -> Self {
+        Corners {
+            top_left: val.into(),
+            top_right: val.into(),
+            bottom_right: val.into(),
+            bottom_left: val.into(),
+        }
+    }
+}
+
+impl From<Pixels> for Corners<Pixels> {
+    fn from(val: Pixels) -> Self {
+        Corners {
+            top_left: val,
+            top_right: val,
+            bottom_right: val,
+            bottom_left: val,
+        }
+    }
+}
+
+/// Represents a length in pixels, the base unit of measurement in the UI framework.
+///
+/// `Pixels` is a value type that represents an absolute length in pixels, which is used
+/// for specifying sizes, positions, and distances in the UI. It is the fundamental unit
+/// of measurement for all visual elements and layout calculations.
+///
+/// The inner value is an `f32`, allowing for sub-pixel precision which can be useful for
+/// anti-aliasing and animations. However, when applied to actual pixel grids, the value
+/// is typically rounded to the nearest integer.
+///
+/// # Examples
+///
+/// ```
+/// use zed::Pixels;
+///
+/// // Define a length of 10 pixels
+/// let length = Pixels(10.0);
+///
+/// // Define a length and scale it by a factor of 2
+/// let scaled_length = length.scale(2.0);
+/// assert_eq!(scaled_length, Pixels(20.0));
+/// ```
+#[derive(
+    Clone,
+    Copy,
+    Default,
+    Add,
+    AddAssign,
+    Sub,
+    SubAssign,
+    Neg,
+    Div,
+    DivAssign,
+    PartialEq,
+    Serialize,
+    Deserialize,
+)]
+#[repr(transparent)]
+pub struct Pixels(pub f32);
+
+impl std::ops::Div for Pixels {
+    type Output = f32;
+
+    fn div(self, rhs: Self) -> Self::Output {
+        self.0 / rhs.0
+    }
+}
+
+impl std::ops::DivAssign for Pixels {
+    fn div_assign(&mut self, rhs: Self) {
+        *self = Self(self.0 / rhs.0);
+    }
+}
+
+impl std::ops::RemAssign for Pixels {
+    fn rem_assign(&mut self, rhs: Self) {
+        self.0 %= rhs.0;
+    }
 }
 
-impl Default for PathBuilder {
-    fn default() -> Self {
-        PathBuilder::new()
+impl std::ops::Rem for Pixels {
+    type Output = Self;
+
+    fn rem(self, rhs: Self) -> Self {
+        Self(self.0 % rhs.0)
     }
 }
 
-impl PathBuilder {
-    pub fn new() -> Self {
-        Self {
-            vertices: Vec::new(),
-            start: vec2f(0., 0.),
-            current: vec2f(0., 0.),
-            contour_count: 0,
-            bounds: RectF::default(),
-        }
+impl Mul<f32> for Pixels {
+    type Output = Pixels;
+
+    fn mul(self, other: f32) -> Pixels {
+        Pixels(self.0 * other)
     }
+}
 
-    pub fn reset(&mut self, point: Vector2F) {
-        self.vertices.clear();
-        self.start = point;
-        self.current = point;
-        self.contour_count = 0;
+impl Mul<usize> for Pixels {
+    type Output = Pixels;
+
+    fn mul(self, other: usize) -> Pixels {
+        Pixels(self.0 * other as f32)
     }
+}
 
-    pub fn line_to(&mut self, point: Vector2F) {
-        self.contour_count += 1;
-        if self.contour_count > 1 {
-            self.push_triangle(self.start, self.current, point, PathVertexKind::Solid);
-        }
+impl Mul<Pixels> for f32 {
+    type Output = Pixels;
 
-        self.current = point;
+    fn mul(self, rhs: Pixels) -> Self::Output {
+        Pixels(self * rhs.0)
     }
+}
 
-    pub fn curve_to(&mut self, point: Vector2F, ctrl: Vector2F) {
-        self.contour_count += 1;
-        if self.contour_count > 1 {
-            self.push_triangle(self.start, self.current, point, PathVertexKind::Solid);
-        }
+impl MulAssign<f32> for Pixels {
+    fn mul_assign(&mut self, other: f32) {
+        self.0 *= other;
+    }
+}
+
+impl Pixels {
+    /// Represents zero pixels.
+    pub const ZERO: Pixels = Pixels(0.0);
+    /// The maximum value that can be represented by `Pixels`.
+    pub const MAX: Pixels = Pixels(f32::MAX);
 
-        self.push_triangle(self.current, ctrl, point, PathVertexKind::Quadratic);
-        self.current = point;
+    /// Floors the `Pixels` value to the nearest whole number.
+    ///
+    /// # Returns
+    ///
+    /// Returns a new `Pixels` instance with the floored value.
+    pub fn floor(&self) -> Self {
+        Self(self.0.floor())
     }
 
-    pub fn build(mut self, color: Color, clip_bounds: Option<RectF>) -> Path {
-        if let Some(clip_bounds) = clip_bounds {
-            self.bounds = self.bounds.intersection(clip_bounds).unwrap_or_default();
-        }
-        Path {
-            bounds: self.bounds,
-            color,
-            vertices: self.vertices,
-        }
+    /// Rounds the `Pixels` value to the nearest whole number.
+    ///
+    /// # Returns
+    ///
+    /// Returns a new `Pixels` instance with the rounded value.
+    pub fn round(&self) -> Self {
+        Self(self.0.round())
     }
 
-    fn push_triangle(&mut self, a: Vector2F, b: Vector2F, c: Vector2F, kind: PathVertexKind) {
-        if self.vertices.is_empty() {
-            self.bounds = RectF::new(a, Vector2F::zero());
-        }
-        self.bounds = self.bounds.union_point(a).union_point(b).union_point(c);
+    /// Returns the ceiling of the `Pixels` value to the nearest whole number.
+    ///
+    /// # Returns
+    ///
+    /// Returns a new `Pixels` instance with the ceiling value.
+    pub fn ceil(&self) -> Self {
+        Self(self.0.ceil())
+    }
 
-        match kind {
-            PathVertexKind::Solid => {
-                self.vertices.push(PathVertex {
-                    xy_position: a,
-                    st_position: vec2f(0., 1.),
-                });
-                self.vertices.push(PathVertex {
-                    xy_position: b,
-                    st_position: vec2f(0., 1.),
-                });
-                self.vertices.push(PathVertex {
-                    xy_position: c,
-                    st_position: vec2f(0., 1.),
-                });
-            }
-            PathVertexKind::Quadratic => {
-                self.vertices.push(PathVertex {
-                    xy_position: a,
-                    st_position: vec2f(0., 0.),
-                });
-                self.vertices.push(PathVertex {
-                    xy_position: b,
-                    st_position: vec2f(0.5, 0.),
-                });
-                self.vertices.push(PathVertex {
-                    xy_position: c,
-                    st_position: vec2f(1., 1.),
-                });
-            }
-        }
+    /// Scales the `Pixels` value by a given factor, producing `ScaledPixels`.
+    ///
+    /// This method is used when adjusting pixel values for display scaling factors,
+    /// such as high DPI (dots per inch) or Retina displays, where the pixel density is higher and
+    /// thus requires scaling to maintain visual consistency and readability.
+    ///
+    /// The resulting `ScaledPixels` represent the scaled value which can be used for rendering
+    /// calculations where display scaling is considered.
+    pub fn scale(&self, factor: f32) -> ScaledPixels {
+        ScaledPixels(self.0 * factor)
+    }
+
+    /// Raises the `Pixels` value to a given power.
+    ///
+    /// # Arguments
+    ///
+    /// * `exponent` - The exponent to raise the `Pixels` value by.
+    ///
+    /// # Returns
+    ///
+    /// Returns a new `Pixels` instance with the value raised to the given exponent.
+    pub fn pow(&self, exponent: f32) -> Self {
+        Self(self.0.powf(exponent))
+    }
+
+    /// Returns the absolute value of the `Pixels`.
+    ///
+    /// # Returns
+    ///
+    /// A new `Pixels` instance with the absolute value of the original `Pixels`.
+    pub fn abs(&self) -> Self {
+        Self(self.0.abs())
     }
 }
 
-pub fn deserialize_vec2f<'de, D>(deserializer: D) -> Result<Vector2F, D::Error>
-where
-    D: Deserializer<'de>,
-{
-    let [x, y]: [f32; 2] = Deserialize::deserialize(deserializer)?;
-    Ok(vec2f(x, y))
+impl Mul<Pixels> for Pixels {
+    type Output = Pixels;
+
+    fn mul(self, rhs: Pixels) -> Self::Output {
+        Pixels(self.0 * rhs.0)
+    }
 }
 
-impl ToJson for Vector2F {
-    fn to_json(&self) -> serde_json::Value {
-        json!([self.x(), self.y()])
+impl Eq for Pixels {}
+
+impl PartialOrd for Pixels {
+    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
+        self.0.partial_cmp(&other.0)
     }
 }
 
-impl ToJson for RectF {
-    fn to_json(&self) -> serde_json::Value {
-        json!({"origin": self.origin().to_json(), "size": self.size().to_json()})
+impl Ord for Pixels {
+    fn cmp(&self, other: &Self) -> cmp::Ordering {
+        self.partial_cmp(other).unwrap()
     }
 }
 
-#[derive(Refineable, Debug)]
-#[refineable(Debug)]
-pub struct Point<T: Clone + Default + Debug> {
-    pub x: T,
-    pub y: T,
+impl std::hash::Hash for Pixels {
+    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+        self.0.to_bits().hash(state);
+    }
 }
 
-impl<T: Clone + Default + Debug> Clone for Point<T> {
-    fn clone(&self) -> Self {
-        Self {
-            x: self.x.clone(),
-            y: self.y.clone(),
-        }
+impl From<f64> for Pixels {
+    fn from(pixels: f64) -> Self {
+        Pixels(pixels as f32)
     }
 }
 
-impl<T: Clone + Default + Debug> Into<taffy::geometry::Point<T>> for Point<T> {
-    fn into(self) -> taffy::geometry::Point<T> {
-        taffy::geometry::Point {
-            x: self.x,
-            y: self.y,
-        }
+impl From<f32> for Pixels {
+    fn from(pixels: f32) -> Self {
+        Pixels(pixels)
     }
 }
 
-#[derive(Refineable, Clone, Debug)]
-#[refineable(Debug)]
-pub struct Size<T: Clone + Default + Debug> {
-    pub width: T,
-    pub height: T,
+impl Debug for Pixels {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{} px", self.0)
+    }
 }
 
-impl<S, T: Clone + Default + Debug> From<taffy::geometry::Size<S>> for Size<T>
-where
-    S: Into<T>,
-{
-    fn from(value: taffy::geometry::Size<S>) -> Self {
-        Self {
-            width: value.width.into(),
-            height: value.height.into(),
-        }
+impl From<Pixels> for f32 {
+    fn from(pixels: Pixels) -> Self {
+        pixels.0
     }
 }
 
-impl<S, T: Clone + Default + Debug> Into<taffy::geometry::Size<S>> for Size<T>
-where
-    T: Into<S>,
-{
-    fn into(self) -> taffy::geometry::Size<S> {
-        taffy::geometry::Size {
-            width: self.width.into(),
-            height: self.height.into(),
-        }
+impl From<&Pixels> for f32 {
+    fn from(pixels: &Pixels) -> Self {
+        pixels.0
     }
 }
 
-impl Size<DefiniteLength> {
-    pub fn zero() -> Self {
-        Self {
-            width: pixels(0.).into(),
-            height: pixels(0.).into(),
-        }
+impl From<Pixels> for f64 {
+    fn from(pixels: Pixels) -> Self {
+        pixels.0 as f64
     }
+}
 
-    pub fn to_taffy(&self, rem_size: f32) -> taffy::geometry::Size<taffy::style::LengthPercentage> {
-        taffy::geometry::Size {
-            width: self.width.to_taffy(rem_size),
-            height: self.height.to_taffy(rem_size),
-        }
+impl From<Pixels> for u32 {
+    fn from(pixels: Pixels) -> Self {
+        pixels.0 as u32
     }
 }
 
-impl Size<Length> {
-    pub fn auto() -> Self {
-        Self {
-            width: Length::Auto,
-            height: Length::Auto,
-        }
+impl From<u32> for Pixels {
+    fn from(pixels: u32) -> Self {
+        Pixels(pixels as f32)
     }
+}
 
-    pub fn to_taffy<T: From<taffy::prelude::LengthPercentageAuto>>(
-        &self,
-        rem_size: f32,
-    ) -> taffy::geometry::Size<T> {
-        taffy::geometry::Size {
-            width: self.width.to_taffy(rem_size).into(),
-            height: self.height.to_taffy(rem_size).into(),
-        }
+impl From<Pixels> for usize {
+    fn from(pixels: Pixels) -> Self {
+        pixels.0 as usize
     }
 }
 
-#[derive(Clone, Default, Refineable, Debug)]
-#[refineable(Debug)]
-pub struct Edges<T: Clone + Default + Debug> {
-    pub top: T,
-    pub right: T,
-    pub bottom: T,
-    pub left: T,
+impl From<usize> for Pixels {
+    fn from(pixels: usize) -> Self {
+        Pixels(pixels as f32)
+    }
 }
 
-impl<T: Clone + Default + Debug> Edges<T> {
-    pub fn uniform(value: T) -> Self {
-        Self {
-            top: value.clone(),
-            right: value.clone(),
-            bottom: value.clone(),
-            left: value.clone(),
-        }
+/// Represents physical pixels on the display.
+///
+/// `DevicePixels` is a unit of measurement that refers to the actual pixels on a device's screen.
+/// This type is used when precise pixel manipulation is required, such as rendering graphics or
+/// interfacing with hardware that operates on the pixel level. Unlike logical pixels that may be
+/// affected by the device's scale factor, `DevicePixels` always correspond to real pixels on the
+/// display.
+#[derive(
+    Add, AddAssign, Clone, Copy, Default, Div, Eq, Hash, Ord, PartialEq, PartialOrd, Sub, SubAssign,
+)]
+#[repr(transparent)]
+pub struct DevicePixels(pub(crate) i32);
+
+impl DevicePixels {
+    /// Converts the `DevicePixels` value to the number of bytes needed to represent it in memory.
+    ///
+    /// This function is useful when working with graphical data that needs to be stored in a buffer,
+    /// such as images or framebuffers, where each pixel may be represented by a specific number of bytes.
+    ///
+    /// # Arguments
+    ///
+    /// * `bytes_per_pixel` - The number of bytes used to represent a single pixel.
+    ///
+    /// # Returns
+    ///
+    /// The number of bytes required to represent the `DevicePixels` value in memory.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::DevicePixels;
+    /// let pixels = DevicePixels(10); // 10 device pixels
+    /// let bytes_per_pixel = 4; // Assume each pixel is represented by 4 bytes (e.g., RGBA)
+    /// let total_bytes = pixels.to_bytes(bytes_per_pixel);
+    /// assert_eq!(total_bytes, 40); // 10 pixels * 4 bytes/pixel = 40 bytes
+    /// ```
+    pub fn to_bytes(&self, bytes_per_pixel: u8) -> u32 {
+        self.0 as u32 * bytes_per_pixel as u32
     }
 }
 
-impl Edges<Length> {
-    pub fn auto() -> Self {
-        Self {
-            top: Length::Auto,
-            right: Length::Auto,
-            bottom: Length::Auto,
-            left: Length::Auto,
-        }
+impl fmt::Debug for DevicePixels {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{} px (device)", self.0)
     }
+}
 
-    pub fn zero() -> Self {
-        Self {
-            top: pixels(0.).into(),
-            right: pixels(0.).into(),
-            bottom: pixels(0.).into(),
-            left: pixels(0.).into(),
-        }
+impl From<DevicePixels> for i32 {
+    fn from(device_pixels: DevicePixels) -> Self {
+        device_pixels.0
     }
+}
 
-    pub fn to_taffy(
-        &self,
-        rem_size: f32,
-    ) -> taffy::geometry::Rect<taffy::style::LengthPercentageAuto> {
-        taffy::geometry::Rect {
-            top: self.top.to_taffy(rem_size),
-            right: self.right.to_taffy(rem_size),
-            bottom: self.bottom.to_taffy(rem_size),
-            left: self.left.to_taffy(rem_size),
-        }
+impl From<i32> for DevicePixels {
+    fn from(device_pixels: i32) -> Self {
+        DevicePixels(device_pixels)
     }
 }
 
-impl Edges<DefiniteLength> {
-    pub fn zero() -> Self {
-        Self {
-            top: pixels(0.).into(),
-            right: pixels(0.).into(),
-            bottom: pixels(0.).into(),
-            left: pixels(0.).into(),
-        }
+impl From<u32> for DevicePixels {
+    fn from(device_pixels: u32) -> Self {
+        DevicePixels(device_pixels as i32)
     }
+}
 
-    pub fn to_taffy(&self, rem_size: f32) -> taffy::geometry::Rect<taffy::style::LengthPercentage> {
-        taffy::geometry::Rect {
-            top: self.top.to_taffy(rem_size),
-            right: self.right.to_taffy(rem_size),
-            bottom: self.bottom.to_taffy(rem_size),
-            left: self.left.to_taffy(rem_size),
-        }
+impl From<DevicePixels> for u32 {
+    fn from(device_pixels: DevicePixels) -> Self {
+        device_pixels.0 as u32
     }
 }
 
-impl Edges<AbsoluteLength> {
-    pub fn zero() -> Self {
-        Self {
-            top: pixels(0.),
-            right: pixels(0.),
-            bottom: pixels(0.),
-            left: pixels(0.),
-        }
+impl From<DevicePixels> for u64 {
+    fn from(device_pixels: DevicePixels) -> Self {
+        device_pixels.0 as u64
     }
+}
 
-    pub fn to_taffy(&self, rem_size: f32) -> taffy::geometry::Rect<taffy::style::LengthPercentage> {
-        taffy::geometry::Rect {
-            top: self.top.to_taffy(rem_size),
-            right: self.right.to_taffy(rem_size),
-            bottom: self.bottom.to_taffy(rem_size),
-            left: self.left.to_taffy(rem_size),
-        }
+impl From<u64> for DevicePixels {
+    fn from(device_pixels: u64) -> Self {
+        DevicePixels(device_pixels as i32)
     }
+}
 
-    pub fn to_pixels(&self, rem_size: f32) -> Edges<f32> {
-        Edges {
-            top: self.top.to_pixels(rem_size),
-            right: self.right.to_pixels(rem_size),
-            bottom: self.bottom.to_pixels(rem_size),
-            left: self.left.to_pixels(rem_size),
-        }
+impl From<DevicePixels> for usize {
+    fn from(device_pixels: DevicePixels) -> Self {
+        device_pixels.0 as usize
     }
 }
 
-impl Edges<f32> {
-    pub fn is_empty(&self) -> bool {
-        self.top == 0.0 && self.right == 0.0 && self.bottom == 0.0 && self.left == 0.0
+impl From<usize> for DevicePixels {
+    fn from(device_pixels: usize) -> Self {
+        DevicePixels(device_pixels as i32)
     }
 }
 
-#[derive(Clone, Copy, Neg)]
-pub enum AbsoluteLength {
-    Pixels(f32),
-    Rems(f32),
+/// Represents scaled pixels that take into account the device's scale factor.
+///
+/// `ScaledPixels` are used to ensure that UI elements appear at the correct size on devices
+/// with different pixel densities. When a device has a higher scale factor (such as Retina displays),
+/// a single logical pixel may correspond to multiple physical pixels. By using `ScaledPixels`,
+/// dimensions and positions can be specified in a way that scales appropriately across different
+/// display resolutions.
+#[derive(Clone, Copy, Default, Add, AddAssign, Sub, SubAssign, Div, PartialEq, PartialOrd)]
+#[repr(transparent)]
+pub struct ScaledPixels(pub(crate) f32);
+
+impl ScaledPixels {
+    /// Floors the `ScaledPixels` value to the nearest whole number.
+    ///
+    /// # Returns
+    ///
+    /// Returns a new `ScaledPixels` instance with the floored value.
+    pub fn floor(&self) -> Self {
+        Self(self.0.floor())
+    }
+
+    /// Rounds the `ScaledPixels` value to the nearest whole number.
+    ///
+    /// # Returns
+    ///
+    /// Returns a new `ScaledPixels` instance with the rounded value.
+    pub fn ceil(&self) -> Self {
+        Self(self.0.ceil())
+    }
 }
 
-impl std::fmt::Debug for AbsoluteLength {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match self {
-            AbsoluteLength::Pixels(pixels) => write!(f, "{}px", pixels),
-            AbsoluteLength::Rems(rems) => write!(f, "{}rems", rems),
-        }
+impl Eq for ScaledPixels {}
+
+impl Debug for ScaledPixels {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{} px (scaled)", self.0)
+    }
+}
+
+impl From<ScaledPixels> for DevicePixels {
+    fn from(scaled: ScaledPixels) -> Self {
+        DevicePixels(scaled.0.ceil() as i32)
+    }
+}
+
+impl From<DevicePixels> for ScaledPixels {
+    fn from(device: DevicePixels) -> Self {
+        ScaledPixels(device.0 as f32)
     }
 }
 
+impl From<ScaledPixels> for f64 {
+    fn from(scaled_pixels: ScaledPixels) -> Self {
+        scaled_pixels.0 as f64
+    }
+}
+
+/// Represents pixels in a global coordinate space, which can span across multiple displays.
+///
+/// `GlobalPixels` is used when dealing with a coordinate system that is not limited to a single
+/// display's boundaries. This type is particularly useful in multi-monitor setups where
+/// positioning and measurements need to be consistent and relative to a "global" origin point
+/// rather than being relative to any individual display.
+#[derive(Clone, Copy, Default, Add, AddAssign, Sub, SubAssign, Div, PartialEq, PartialOrd)]
+#[repr(transparent)]
+pub struct GlobalPixels(pub(crate) f32);
+
+impl Debug for GlobalPixels {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{} px (global coordinate space)", self.0)
+    }
+}
+
+impl From<GlobalPixels> for f64 {
+    fn from(global_pixels: GlobalPixels) -> Self {
+        global_pixels.0 as f64
+    }
+}
+
+impl From<f64> for GlobalPixels {
+    fn from(global_pixels: f64) -> Self {
+        GlobalPixels(global_pixels as f32)
+    }
+}
+
+impl sqlez::bindable::StaticColumnCount for GlobalPixels {}
+
+impl sqlez::bindable::Bind for GlobalPixels {
+    fn bind(
+        &self,
+        statement: &sqlez::statement::Statement,
+        start_index: i32,
+    ) -> anyhow::Result<i32> {
+        self.0.bind(statement, start_index)
+    }
+}
+
+/// Represents a length in rems, a unit based on the font-size of the window, which can be assigned with [WindowContext::set_rem_size].
+///
+/// Rems are used for defining lengths that are scalable and consistent across different UI elements.
+/// The value of `1rem` is typically equal to the font-size of the root element (often the `<html>` element in browsers),
+/// making it a flexible unit that adapts to the user's text size preferences. In this framework, `rems` serve a similar
+/// purpose, allowing for scalable and accessible design that can adjust to different display settings or user preferences.
+///
+/// For example, if the root element's font-size is `16px`, then `1rem` equals `16px`. A length of `2rems` would then be `32px`.
+#[derive(Clone, Copy, Default, Add, Sub, Mul, Div, Neg)]
+pub struct Rems(pub f32);
+
+impl Mul<Pixels> for Rems {
+    type Output = Pixels;
+
+    fn mul(self, other: Pixels) -> Pixels {
+        Pixels(self.0 * other.0)
+    }
+}
+
+impl Debug for Rems {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{} rem", self.0)
+    }
+}
+
+/// Represents an absolute length in pixels or rems.
+///
+/// `AbsoluteLength` can be either a fixed number of pixels, which is an absolute measurement not
+/// affected by the current font size, or a number of rems, which is relative to the font size of
+/// the root element. It is used for specifying dimensions that are either independent of or
+/// related to the typographic scale.
+#[derive(Clone, Copy, Debug, Neg)]
+pub enum AbsoluteLength {
+    /// A length in pixels.
+    Pixels(Pixels),
+    /// A length in rems.
+    Rems(Rems),
+}
+
 impl AbsoluteLength {
-    pub fn to_pixels(&self, rem_size: f32) -> f32 {
+    /// Checks if the absolute length is zero.
+    pub fn is_zero(&self) -> bool {
         match self {
-            AbsoluteLength::Pixels(pixels) => *pixels,
-            AbsoluteLength::Rems(rems) => rems * rem_size,
+            AbsoluteLength::Pixels(px) => px.0 == 0.0,
+            AbsoluteLength::Rems(rems) => rems.0 == 0.0,
         }
     }
+}
+
+impl From<Pixels> for AbsoluteLength {
+    fn from(pixels: Pixels) -> Self {
+        AbsoluteLength::Pixels(pixels)
+    }
+}
+
+impl From<Rems> for AbsoluteLength {
+    fn from(rems: Rems) -> Self {
+        AbsoluteLength::Rems(rems)
+    }
+}
 
-    pub fn to_taffy(&self, rem_size: f32) -> taffy::style::LengthPercentage {
+impl AbsoluteLength {
+    /// Converts an `AbsoluteLength` to `Pixels` based on a given `rem_size`.
+    ///
+    /// # Arguments
+    ///
+    /// * `rem_size` - The size of one rem in pixels.
+    ///
+    /// # Returns
+    ///
+    /// Returns the `AbsoluteLength` as `Pixels`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::{AbsoluteLength, Pixels};
+    /// let length_in_pixels = AbsoluteLength::Pixels(Pixels(42.0));
+    /// let length_in_rems = AbsoluteLength::Rems(Rems(2.0));
+    /// let rem_size = Pixels(16.0);
+    ///
+    /// assert_eq!(length_in_pixels.to_pixels(rem_size), Pixels(42.0));
+    /// assert_eq!(length_in_rems.to_pixels(rem_size), Pixels(32.0));
+    /// ```
+    pub fn to_pixels(&self, rem_size: Pixels) -> Pixels {
         match self {
-            AbsoluteLength::Pixels(pixels) => taffy::style::LengthPercentage::Length(*pixels),
-            AbsoluteLength::Rems(rems) => taffy::style::LengthPercentage::Length(rems * rem_size),
+            AbsoluteLength::Pixels(pixels) => *pixels,
+            AbsoluteLength::Rems(rems) => *rems * rem_size,
         }
     }
 }
 
 impl Default for AbsoluteLength {
     fn default() -> Self {
-        Self::Pixels(0.0)
+        px(0.).into()
     }
 }
 
 /// A non-auto length that can be defined in pixels, rems, or percent of parent.
+///
+/// This enum represents lengths that have a specific value, as opposed to lengths that are automatically
+/// determined by the context. It includes absolute lengths in pixels or rems, and relative lengths as a
+/// fraction of the parent's size.
 #[derive(Clone, Copy, Neg)]
 pub enum DefiniteLength {
+    /// An absolute length specified in pixels or rems.
     Absolute(AbsoluteLength),
-    Relative(f32), // 0. to 1.
+    /// A relative length specified as a fraction of the parent's size, between 0 and 1.
+    Fraction(f32),
 }
 
 impl DefiniteLength {
-    fn to_taffy(&self, rem_size: f32) -> taffy::style::LengthPercentage {
+    /// Converts the `DefiniteLength` to `Pixels` based on a given `base_size` and `rem_size`.
+    ///
+    /// If the `DefiniteLength` is an absolute length, it will be directly converted to `Pixels`.
+    /// If it is a fraction, the fraction will be multiplied by the `base_size` to get the length in pixels.
+    ///
+    /// # Arguments
+    ///
+    /// * `base_size` - The base size in `AbsoluteLength` to which the fraction will be applied.
+    /// * `rem_size` - The size of one rem in pixels, used to convert rems to pixels.
+    ///
+    /// # Returns
+    ///
+    /// Returns the `DefiniteLength` as `Pixels`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use zed::{DefiniteLength, AbsoluteLength, Pixels, px, rems};
+    /// let length_in_pixels = DefiniteLength::Absolute(AbsoluteLength::Pixels(px(42.0)));
+    /// let length_in_rems = DefiniteLength::Absolute(AbsoluteLength::Rems(rems(2.0)));
+    /// let length_as_fraction = DefiniteLength::Fraction(0.5);
+    /// let base_size = AbsoluteLength::Pixels(px(100.0));
+    /// let rem_size = px(16.0);
+    ///
+    /// assert_eq!(length_in_pixels.to_pixels(base_size, rem_size), Pixels(42.0));
+    /// assert_eq!(length_in_rems.to_pixels(base_size, rem_size), Pixels(32.0));
+    /// assert_eq!(length_as_fraction.to_pixels(base_size, rem_size), Pixels(50.0));
+    /// ```
+    pub fn to_pixels(&self, base_size: AbsoluteLength, rem_size: Pixels) -> Pixels {
         match self {
-            DefiniteLength::Absolute(length) => match length {
-                AbsoluteLength::Pixels(pixels) => taffy::style::LengthPercentage::Length(*pixels),
-                AbsoluteLength::Rems(rems) => {
-                    taffy::style::LengthPercentage::Length(rems * rem_size)
-                }
+            DefiniteLength::Absolute(size) => size.to_pixels(rem_size),
+            DefiniteLength::Fraction(fraction) => match base_size {
+                AbsoluteLength::Pixels(px) => px * *fraction,
+                AbsoluteLength::Rems(rems) => rems * rem_size * *fraction,
             },
-            DefiniteLength::Relative(fraction) => {
-                taffy::style::LengthPercentage::Percent(*fraction)
-            }
         }
     }
 }
 
-impl std::fmt::Debug for DefiniteLength {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+impl Debug for DefiniteLength {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
-            DefiniteLength::Absolute(length) => std::fmt::Debug::fmt(length, f),
-            DefiniteLength::Relative(fract) => write!(f, "{}%", (fract * 100.0) as i32),
+            DefiniteLength::Absolute(length) => Debug::fmt(length, f),
+            DefiniteLength::Fraction(fract) => write!(f, "{}%", (fract * 100.0) as i32),
         }
     }
 }
 
+impl From<Pixels> for DefiniteLength {
+    fn from(pixels: Pixels) -> Self {
+        Self::Absolute(pixels.into())
+    }
+}
+
+impl From<Rems> for DefiniteLength {
+    fn from(rems: Rems) -> Self {
+        Self::Absolute(rems.into())
+    }
+}
+
 impl From<AbsoluteLength> for DefiniteLength {
     fn from(length: AbsoluteLength) -> Self {
         Self::Absolute(length)

crates/gpui/src/gpui.rs 🔗

@@ -1,40 +1,215 @@
+#[macro_use]
+mod action;
 mod app;
-mod image_cache;
-pub use app::*;
+
+mod arena;
 mod assets;
+mod color;
+mod element;
+mod elements;
+mod executor;
+mod geometry;
+mod image_cache;
+mod input;
+mod interactive;
+mod key_dispatch;
+mod keymap;
+mod platform;
+pub mod prelude;
+mod scene;
+mod shared_string;
+mod style;
+mod styled;
+mod subscription;
+mod svg_renderer;
+mod taffy;
 #[cfg(any(test, feature = "test-support"))]
 pub mod test;
-pub use assets::*;
-pub mod elements;
-pub mod font_cache;
-mod image_data;
-pub use crate::image_data::ImageData;
-pub use taffy;
-pub mod views;
-pub use font_cache::FontCache;
-mod clipboard;
-pub use clipboard::ClipboardItem;
-pub mod fonts;
-pub mod geometry;
-pub mod scene;
-pub use scene::{Border, CursorRegion, MouseRegion, MouseRegionId, Quad, Scene, SceneBuilder};
-pub mod text_layout;
-pub use text_layout::TextLayoutCache;
+mod text_system;
 mod util;
-pub use elements::{AnyElement, Element};
-pub mod executor;
-pub use executor::Task;
-pub mod color;
-pub mod json;
-pub mod keymap_matcher;
-pub mod platform;
-pub use gpui_macros::{test, Element};
-pub use usvg;
-pub use window::{
-    Axis, Layout, LayoutEngine, LayoutId, RectFExt, SizeConstraint, Vector2FExt, WindowContext,
-};
+mod view;
+mod window;
+
+mod private {
+    /// A mechanism for restricting implementations of a trait to only those in GPUI.
+    /// See: https://predr.ag/blog/definitive-guide-to-sealed-traits-in-rust/
+    pub trait Sealed {}
+}
 
-pub use anyhow;
+pub use action::*;
+pub use anyhow::Result;
+pub use app::*;
+pub(crate) use arena::*;
+pub use assets::*;
+pub use color::*;
+pub use ctor::ctor;
+pub use element::*;
+pub use elements::*;
+pub use executor::*;
+pub use geometry::*;
+pub use gpui2_macros::*;
+pub use image_cache::*;
+pub use input::*;
+pub use interactive::*;
+pub use key_dispatch::*;
+pub use keymap::*;
+pub use linkme;
+pub use platform::*;
+use private::Sealed;
+pub use refineable::*;
+pub use scene::*;
+pub use serde;
+pub use serde_derive;
 pub use serde_json;
+pub use shared_string::*;
+pub use smallvec;
+pub use smol::Timer;
+pub use style::*;
+pub use styled::*;
+pub use subscription::*;
+pub use svg_renderer::*;
+pub use taffy::{AvailableSpace, LayoutId};
+#[cfg(any(test, feature = "test-support"))]
+pub use test::*;
+pub use text_system::*;
+pub use util::arc_cow::ArcCow;
+pub use view::*;
+pub use window::*;
+
+use std::{
+    any::{Any, TypeId},
+    borrow::BorrowMut,
+};
+use taffy::TaffyLayoutEngine;
+
+pub trait Context {
+    type Result<T>;
+
+    fn new_model<T: 'static>(
+        &mut self,
+        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
+    ) -> Self::Result<Model<T>>;
+
+    fn update_model<T, R>(
+        &mut self,
+        handle: &Model<T>,
+        update: impl FnOnce(&mut T, &mut ModelContext<'_, T>) -> R,
+    ) -> Self::Result<R>
+    where
+        T: 'static;
+
+    fn read_model<T, R>(
+        &self,
+        handle: &Model<T>,
+        read: impl FnOnce(&T, &AppContext) -> R,
+    ) -> Self::Result<R>
+    where
+        T: 'static;
+
+    fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
+    where
+        F: FnOnce(AnyView, &mut WindowContext<'_>) -> T;
+
+    fn read_window<T, R>(
+        &self,
+        window: &WindowHandle<T>,
+        read: impl FnOnce(View<T>, &AppContext) -> R,
+    ) -> Result<R>
+    where
+        T: 'static;
+}
+
+pub trait VisualContext: Context {
+    fn new_view<V>(
+        &mut self,
+        build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
+    ) -> Self::Result<View<V>>
+    where
+        V: 'static + Render;
+
+    fn update_view<V: 'static, R>(
+        &mut self,
+        view: &View<V>,
+        update: impl FnOnce(&mut V, &mut ViewContext<'_, V>) -> R,
+    ) -> Self::Result<R>;
+
+    fn replace_root_view<V>(
+        &mut self,
+        build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
+    ) -> Self::Result<View<V>>
+    where
+        V: 'static + Render;
+
+    fn focus_view<V>(&mut self, view: &View<V>) -> Self::Result<()>
+    where
+        V: FocusableView;
+
+    fn dismiss_view<V>(&mut self, view: &View<V>) -> Self::Result<()>
+    where
+        V: ManagedView;
+}
+
+pub trait Entity<T>: Sealed {
+    type Weak: 'static;
+
+    fn entity_id(&self) -> EntityId;
+    fn downgrade(&self) -> Self::Weak;
+    fn upgrade_from(weak: &Self::Weak) -> Option<Self>
+    where
+        Self: Sized;
+}
+
+pub trait EventEmitter<E: Any>: 'static {}
+
+pub enum GlobalKey {
+    Numeric(usize),
+    View(EntityId),
+    Type(TypeId),
+}
+
+pub trait BorrowAppContext {
+    fn with_text_style<F, R>(&mut self, style: Option<TextStyleRefinement>, f: F) -> R
+    where
+        F: FnOnce(&mut Self) -> R;
+
+    fn set_global<T: 'static>(&mut self, global: T);
+}
+
+impl<C> BorrowAppContext for C
+where
+    C: BorrowMut<AppContext>,
+{
+    fn with_text_style<F, R>(&mut self, style: Option<TextStyleRefinement>, f: F) -> R
+    where
+        F: FnOnce(&mut Self) -> R,
+    {
+        if let Some(style) = style {
+            self.borrow_mut().push_text_style(style);
+            let result = f(self);
+            self.borrow_mut().pop_text_style();
+            result
+        } else {
+            f(self)
+        }
+    }
+
+    fn set_global<G: 'static>(&mut self, global: G) {
+        self.borrow_mut().set_global(global)
+    }
+}
+
+pub trait Flatten<T> {
+    fn flatten(self) -> Result<T>;
+}
+
+impl<T> Flatten<T> for Result<Result<T>> {
+    fn flatten(self) -> Result<T> {
+        self?
+    }
+}
 
-actions!(zed, [NoAction]);
+impl<T> Flatten<T> for Result<T> {
+    fn flatten(self) -> Result<T> {
+        self
+    }
+}

crates/gpui/src/image_cache.rs 🔗

@@ -1,18 +1,19 @@
-use std::sync::Arc;
-
-use crate::ImageData;
+use crate::{ImageData, ImageId, SharedString};
 use collections::HashMap;
 use futures::{
     future::{BoxFuture, Shared},
-    AsyncReadExt, FutureExt,
+    AsyncReadExt, FutureExt, TryFutureExt,
 };
 use image::ImageError;
 use parking_lot::Mutex;
+use std::sync::Arc;
 use thiserror::Error;
-use util::{
-    arc_cow::ArcCow,
-    http::{self, HttpClient},
-};
+use util::http::{self, HttpClient};
+
+#[derive(PartialEq, Eq, Hash, Clone)]
+pub struct RenderImageParams {
+    pub(crate) image_id: ImageId,
+}
 
 #[derive(Debug, Error, Clone)]
 pub enum Error {
@@ -43,7 +44,7 @@ impl From<ImageError> for Error {
 
 pub struct ImageCache {
     client: Arc<dyn HttpClient>,
-    images: Arc<Mutex<HashMap<ArcCow<'static, str>, FetchImageFuture>>>,
+    images: Arc<Mutex<HashMap<SharedString, FetchImageFuture>>>,
 }
 
 type FetchImageFuture = Shared<BoxFuture<'static, Result<Arc<ImageData>, Error>>>;
@@ -58,12 +59,12 @@ impl ImageCache {
 
     pub fn get(
         &self,
-        uri: impl Into<ArcCow<'static, str>>,
+        uri: impl Into<SharedString>,
     ) -> Shared<BoxFuture<'static, Result<Arc<ImageData>, Error>>> {
         let uri = uri.into();
         let mut images = self.images.lock();
 
-        match images.get(uri.as_ref()) {
+        match images.get(&uri) {
             Some(future) => future.clone(),
             None => {
                 let client = self.client.clone();
@@ -84,9 +85,17 @@ impl ImageCache {
                         let format = image::guess_format(&body)?;
                         let image =
                             image::load_from_memory_with_format(&body, format)?.into_bgra8();
-                        Ok(ImageData::new(image))
+                        Ok(Arc::new(ImageData::new(image)))
                     }
                 }
+                .map_err({
+                    let uri = uri.clone();
+
+                    move |error| {
+                        log::log!(log::Level::Error, "{:?} {:?}", &uri, &error);
+                        error
+                    }
+                })
                 .boxed()
                 .shared();
 

crates/gpui/src/image_data.rs 🔗

@@ -1,43 +0,0 @@
-use crate::geometry::vector::{vec2i, Vector2I};
-use image::{Bgra, ImageBuffer};
-use std::{
-    fmt,
-    sync::{
-        atomic::{AtomicUsize, Ordering::SeqCst},
-        Arc,
-    },
-};
-
-pub struct ImageData {
-    pub id: usize,
-    data: ImageBuffer<Bgra<u8>, Vec<u8>>,
-}
-
-impl ImageData {
-    pub fn new(data: ImageBuffer<Bgra<u8>, Vec<u8>>) -> Arc<Self> {
-        static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
-
-        Arc::new(Self {
-            id: NEXT_ID.fetch_add(1, SeqCst),
-            data,
-        })
-    }
-
-    pub fn as_bytes(&self) -> &[u8] {
-        &self.data
-    }
-
-    pub fn size(&self) -> Vector2I {
-        let (width, height) = self.data.dimensions();
-        vec2i(width as i32, height as i32)
-    }
-}
-
-impl fmt::Debug for ImageData {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("ImageData")
-            .field("id", &self.id)
-            .field("size", &self.data.dimensions())
-            .finish()
-    }
-}

crates/gpui/src/json.rs 🔗

@@ -1,15 +0,0 @@
-pub use serde_json::*;
-
-pub trait ToJson {
-    fn to_json(&self) -> Value;
-}
-
-impl<T: ToJson> ToJson for Option<T> {
-    fn to_json(&self) -> Value {
-        if let Some(value) = self.as_ref() {
-            value.to_json()
-        } else {
-            json!(null)
-        }
-    }
-}

crates/gpui/src/keymap_matcher.rs 🔗

@@ -1,587 +0,0 @@
-mod binding;
-mod keymap;
-mod keymap_context;
-mod keystroke;
-
-use std::{any::TypeId, fmt::Debug};
-
-use collections::HashMap;
-use smallvec::SmallVec;
-
-use crate::{Action, NoAction};
-
-pub use binding::{Binding, BindingMatchResult};
-pub use keymap::Keymap;
-pub use keymap_context::{KeymapContext, KeymapContextPredicate};
-pub use keystroke::Keystroke;
-
-pub struct KeymapMatcher {
-    pub contexts: Vec<KeymapContext>,
-    pending_views: HashMap<usize, KeymapContext>,
-    pending_keystrokes: Vec<Keystroke>,
-    keymap: Keymap,
-}
-
-impl KeymapMatcher {
-    pub fn new(keymap: Keymap) -> Self {
-        Self {
-            contexts: Vec::new(),
-            pending_views: Default::default(),
-            pending_keystrokes: Vec::new(),
-            keymap,
-        }
-    }
-
-    pub fn set_keymap(&mut self, keymap: Keymap) {
-        self.clear_pending();
-        self.keymap = keymap;
-    }
-
-    pub fn add_bindings<T: IntoIterator<Item = Binding>>(&mut self, bindings: T) {
-        self.clear_pending();
-        self.keymap.add_bindings(bindings);
-    }
-
-    pub fn clear_bindings(&mut self) {
-        self.clear_pending();
-        self.keymap.clear();
-    }
-
-    pub fn bindings_for_action(&self, action_id: TypeId) -> impl Iterator<Item = &Binding> {
-        self.keymap.bindings_for_action(action_id)
-    }
-
-    pub fn clear_pending(&mut self) {
-        self.pending_keystrokes.clear();
-        self.pending_views.clear();
-    }
-
-    pub fn has_pending_keystrokes(&self) -> bool {
-        !self.pending_keystrokes.is_empty()
-    }
-
-    /// Pushes a keystroke onto the matcher.
-    /// The result of the new keystroke is returned:
-    ///     MatchResult::None =>
-    ///         No match is valid for this key given any pending keystrokes.
-    ///     MatchResult::Pending =>
-    ///         There exist bindings which are still waiting for more keys.
-    ///     MatchResult::Complete(matches) =>
-    ///         1 or more bindings have received the necessary key presses.
-    ///         The order of the matched actions is by position of the matching first,
-    //          and order in the keymap second.
-    pub fn push_keystroke(
-        &mut self,
-        keystroke: Keystroke,
-        mut dispatch_path: Vec<(usize, KeymapContext)>,
-    ) -> MatchResult {
-        // Collect matched bindings into an ordered list using the position in the matching binding first,
-        // and then the order the binding matched in the view tree second.
-        // The key is the reverse position of the binding in the bindings list so that later bindings
-        // match before earlier ones in the user's config
-        let mut matched_bindings: Vec<(usize, Box<dyn Action>)> = Default::default();
-        let no_action_id = (NoAction {}).id();
-
-        let first_keystroke = self.pending_keystrokes.is_empty();
-        let mut pending_key = None;
-        let mut previous_keystrokes = self.pending_keystrokes.clone();
-
-        self.contexts.clear();
-        self.contexts
-            .extend(dispatch_path.iter_mut().map(|e| std::mem::take(&mut e.1)));
-
-        // Find the bindings which map the pending keystrokes and current context
-        for (i, (view_id, _)) in dispatch_path.iter().enumerate() {
-            // Don't require pending view entry if there are no pending keystrokes
-            if !first_keystroke && !self.pending_views.contains_key(view_id) {
-                continue;
-            }
-
-            // If there is a previous view context, invalidate that view if it
-            // has changed
-            if let Some(previous_view_context) = self.pending_views.remove(view_id) {
-                if previous_view_context != self.contexts[i] {
-                    continue;
-                }
-            }
-
-            for binding in self.keymap.bindings().iter().rev() {
-                for possibility in keystroke.match_possibilities() {
-                    previous_keystrokes.push(possibility.clone());
-                    match binding.match_keys_and_context(&previous_keystrokes, &self.contexts[i..])
-                    {
-                        BindingMatchResult::Complete(action) => {
-                            if action.id() != no_action_id {
-                                matched_bindings.push((*view_id, action));
-                            }
-                        }
-                        BindingMatchResult::Partial => {
-                            if pending_key == None || pending_key == Some(possibility.clone()) {
-                                self.pending_views
-                                    .insert(*view_id, self.contexts[i].clone());
-                                pending_key = Some(possibility)
-                            }
-                        }
-                        _ => {}
-                    }
-                    previous_keystrokes.pop();
-                }
-            }
-        }
-
-        if pending_key.is_some() {
-            self.pending_keystrokes.push(pending_key.unwrap());
-        } else {
-            self.clear_pending();
-        }
-
-        if !matched_bindings.is_empty() {
-            // Collect the sorted matched bindings into the final vec for ease of use
-            // Matched bindings are in order by precedence
-            MatchResult::Matches(matched_bindings)
-        } else if !self.pending_keystrokes.is_empty() {
-            MatchResult::Pending
-        } else {
-            MatchResult::None
-        }
-    }
-
-    pub fn keystrokes_for_action(
-        &self,
-        action: &dyn Action,
-        contexts: &[KeymapContext],
-    ) -> Option<SmallVec<[Keystroke; 2]>> {
-        self.keymap
-            .bindings()
-            .iter()
-            .rev()
-            .find_map(|binding| binding.keystrokes_for_action(action, contexts))
-    }
-}
-
-impl Default for KeymapMatcher {
-    fn default() -> Self {
-        Self::new(Keymap::default())
-    }
-}
-
-pub enum MatchResult {
-    None,
-    Pending,
-    Matches(Vec<(usize, Box<dyn Action>)>),
-}
-
-impl Debug for MatchResult {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match self {
-            MatchResult::None => f.debug_struct("MatchResult::None").finish(),
-            MatchResult::Pending => f.debug_struct("MatchResult::Pending").finish(),
-            MatchResult::Matches(matches) => f
-                .debug_list()
-                .entries(
-                    matches
-                        .iter()
-                        .map(|(view_id, action)| format!("{view_id}, {}", action.name())),
-                )
-                .finish(),
-        }
-    }
-}
-
-impl PartialEq for MatchResult {
-    fn eq(&self, other: &Self) -> bool {
-        match (self, other) {
-            (MatchResult::None, MatchResult::None) => true,
-            (MatchResult::Pending, MatchResult::Pending) => true,
-            (MatchResult::Matches(matches), MatchResult::Matches(other_matches)) => {
-                matches.len() == other_matches.len()
-                    && matches.iter().zip(other_matches.iter()).all(
-                        |((view_id, action), (other_view_id, other_action))| {
-                            view_id == other_view_id && action.eq(other_action.as_ref())
-                        },
-                    )
-            }
-            _ => false,
-        }
-    }
-}
-
-impl Eq for MatchResult {}
-
-impl Clone for MatchResult {
-    fn clone(&self) -> Self {
-        match self {
-            MatchResult::None => MatchResult::None,
-            MatchResult::Pending => MatchResult::Pending,
-            MatchResult::Matches(matches) => MatchResult::Matches(
-                matches
-                    .iter()
-                    .map(|(view_id, action)| (*view_id, Action::boxed_clone(action.as_ref())))
-                    .collect(),
-            ),
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use anyhow::Result;
-    use serde::Deserialize;
-
-    use crate::{actions, impl_actions, keymap_matcher::KeymapContext};
-
-    use super::*;
-
-    #[test]
-    fn test_keymap_and_view_ordering() -> Result<()> {
-        actions!(test, [EditorAction, ProjectPanelAction]);
-
-        let mut editor = KeymapContext::default();
-        editor.add_identifier("Editor");
-
-        let mut project_panel = KeymapContext::default();
-        project_panel.add_identifier("ProjectPanel");
-
-        // Editor 'deeper' in than project panel
-        let dispatch_path = vec![(2, editor), (1, project_panel)];
-
-        // But editor actions 'higher' up in keymap
-        let keymap = Keymap::new(vec![
-            Binding::new("left", EditorAction, Some("Editor")),
-            Binding::new("left", ProjectPanelAction, Some("ProjectPanel")),
-        ]);
-
-        let mut matcher = KeymapMatcher::new(keymap);
-
-        assert_eq!(
-            matcher.push_keystroke(Keystroke::parse("left")?, dispatch_path.clone()),
-            MatchResult::Matches(vec![
-                (2, Box::new(EditorAction)),
-                (1, Box::new(ProjectPanelAction)),
-            ]),
-        );
-
-        Ok(())
-    }
-
-    #[test]
-    fn test_push_keystroke() -> Result<()> {
-        actions!(test, [B, AB, C, D, DA, E, EF]);
-
-        let mut context1 = KeymapContext::default();
-        context1.add_identifier("1");
-
-        let mut context2 = KeymapContext::default();
-        context2.add_identifier("2");
-
-        let dispatch_path = vec![(2, context2), (1, context1)];
-
-        let keymap = Keymap::new(vec![
-            Binding::new("a b", AB, Some("1")),
-            Binding::new("b", B, Some("2")),
-            Binding::new("c", C, Some("2")),
-            Binding::new("d", D, Some("1")),
-            Binding::new("d", D, Some("2")),
-            Binding::new("d a", DA, Some("2")),
-        ]);
-
-        let mut matcher = KeymapMatcher::new(keymap);
-
-        // Binding with pending prefix always takes precedence
-        assert_eq!(
-            matcher.push_keystroke(Keystroke::parse("a")?, dispatch_path.clone()),
-            MatchResult::Pending,
-        );
-        // B alone doesn't match because a was pending, so AB is returned instead
-        assert_eq!(
-            matcher.push_keystroke(Keystroke::parse("b")?, dispatch_path.clone()),
-            MatchResult::Matches(vec![(1, Box::new(AB))]),
-        );
-        assert!(!matcher.has_pending_keystrokes());
-
-        // Without an a prefix, B is dispatched like expected
-        assert_eq!(
-            matcher.push_keystroke(Keystroke::parse("b")?, dispatch_path.clone()),
-            MatchResult::Matches(vec![(2, Box::new(B))]),
-        );
-        assert!(!matcher.has_pending_keystrokes());
-
-        // If a is prefixed, C will not be dispatched because there
-        // was a pending binding for it
-        assert_eq!(
-            matcher.push_keystroke(Keystroke::parse("a")?, dispatch_path.clone()),
-            MatchResult::Pending,
-        );
-        assert_eq!(
-            matcher.push_keystroke(Keystroke::parse("c")?, dispatch_path.clone()),
-            MatchResult::None,
-        );
-        assert!(!matcher.has_pending_keystrokes());
-
-        // If a single keystroke matches multiple bindings in the tree
-        // all of them are returned so that we can fallback if the action
-        // handler decides to propagate the action
-        assert_eq!(
-            matcher.push_keystroke(Keystroke::parse("d")?, dispatch_path.clone()),
-            MatchResult::Matches(vec![(2, Box::new(D)), (1, Box::new(D))]),
-        );
-
-        // If none of the d action handlers consume the binding, a pending
-        // binding may then be used
-        assert_eq!(
-            matcher.push_keystroke(Keystroke::parse("a")?, dispatch_path.clone()),
-            MatchResult::Matches(vec![(2, Box::new(DA))]),
-        );
-        assert!(!matcher.has_pending_keystrokes());
-
-        Ok(())
-    }
-
-    #[test]
-    fn test_keystroke_parsing() -> Result<()> {
-        assert_eq!(
-            Keystroke::parse("ctrl-p")?,
-            Keystroke {
-                key: "p".into(),
-                ctrl: true,
-                alt: false,
-                shift: false,
-                cmd: false,
-                function: false,
-                ime_key: None,
-            }
-        );
-
-        assert_eq!(
-            Keystroke::parse("alt-shift-down")?,
-            Keystroke {
-                key: "down".into(),
-                ctrl: false,
-                alt: true,
-                shift: true,
-                cmd: false,
-                function: false,
-                ime_key: None,
-            }
-        );
-
-        assert_eq!(
-            Keystroke::parse("shift-cmd--")?,
-            Keystroke {
-                key: "-".into(),
-                ctrl: false,
-                alt: false,
-                shift: true,
-                cmd: true,
-                function: false,
-                ime_key: None,
-            }
-        );
-
-        Ok(())
-    }
-
-    #[test]
-    fn test_context_predicate_parsing() -> Result<()> {
-        use KeymapContextPredicate::*;
-
-        assert_eq!(
-            KeymapContextPredicate::parse("a && (b == c || d != e)")?,
-            And(
-                Box::new(Identifier("a".into())),
-                Box::new(Or(
-                    Box::new(Equal("b".into(), "c".into())),
-                    Box::new(NotEqual("d".into(), "e".into())),
-                ))
-            )
-        );
-
-        assert_eq!(
-            KeymapContextPredicate::parse("!a")?,
-            Not(Box::new(Identifier("a".into())),)
-        );
-
-        Ok(())
-    }
-
-    #[test]
-    fn test_context_predicate_eval() {
-        let predicate = KeymapContextPredicate::parse("a && b || c == d").unwrap();
-
-        let mut context = KeymapContext::default();
-        context.add_identifier("a");
-        assert!(!predicate.eval(&[context]));
-
-        let mut context = KeymapContext::default();
-        context.add_identifier("a");
-        context.add_identifier("b");
-        assert!(predicate.eval(&[context]));
-
-        let mut context = KeymapContext::default();
-        context.add_identifier("a");
-        context.add_key("c", "x");
-        assert!(!predicate.eval(&[context]));
-
-        let mut context = KeymapContext::default();
-        context.add_identifier("a");
-        context.add_key("c", "d");
-        assert!(predicate.eval(&[context]));
-
-        let predicate = KeymapContextPredicate::parse("!a").unwrap();
-        assert!(predicate.eval(&[KeymapContext::default()]));
-    }
-
-    #[test]
-    fn test_context_child_predicate_eval() {
-        let predicate = KeymapContextPredicate::parse("a && b > c").unwrap();
-        let contexts = [
-            context_set(&["e", "f"]),
-            context_set(&["c", "d"]), // match this context
-            context_set(&["a", "b"]),
-        ];
-
-        assert!(!predicate.eval(&contexts[0..]));
-        assert!(predicate.eval(&contexts[1..]));
-        assert!(!predicate.eval(&contexts[2..]));
-
-        let predicate = KeymapContextPredicate::parse("a && b > c && !d > e").unwrap();
-        let contexts = [
-            context_set(&["f"]),
-            context_set(&["e"]), // only match this context
-            context_set(&["c"]),
-            context_set(&["a", "b"]),
-            context_set(&["e"]),
-            context_set(&["c", "d"]),
-            context_set(&["a", "b"]),
-        ];
-
-        assert!(!predicate.eval(&contexts[0..]));
-        assert!(predicate.eval(&contexts[1..]));
-        assert!(!predicate.eval(&contexts[2..]));
-        assert!(!predicate.eval(&contexts[3..]));
-        assert!(!predicate.eval(&contexts[4..]));
-        assert!(!predicate.eval(&contexts[5..]));
-        assert!(!predicate.eval(&contexts[6..]));
-
-        fn context_set(names: &[&str]) -> KeymapContext {
-            let mut keymap = KeymapContext::new();
-            names
-                .iter()
-                .for_each(|name| keymap.add_identifier(name.to_string()));
-            keymap
-        }
-    }
-
-    #[test]
-    fn test_matcher() -> Result<()> {
-        #[derive(Clone, Deserialize, PartialEq, Eq, Debug)]
-        pub struct A(pub String);
-        impl_actions!(test, [A]);
-        actions!(test, [B, Ab, Dollar, Quote, Ess, Backtick]);
-
-        #[derive(Clone, Debug, Eq, PartialEq)]
-        struct ActionArg {
-            a: &'static str,
-        }
-
-        let keymap = Keymap::new(vec![
-            Binding::new("a", A("x".to_string()), Some("a")),
-            Binding::new("b", B, Some("a")),
-            Binding::new("a b", Ab, Some("a || b")),
-            Binding::new("$", Dollar, Some("a")),
-            Binding::new("\"", Quote, Some("a")),
-            Binding::new("alt-s", Ess, Some("a")),
-            Binding::new("ctrl-`", Backtick, Some("a")),
-        ]);
-
-        let mut context_a = KeymapContext::default();
-        context_a.add_identifier("a");
-
-        let mut context_b = KeymapContext::default();
-        context_b.add_identifier("b");
-
-        let mut matcher = KeymapMatcher::new(keymap);
-
-        // Basic match
-        assert_eq!(
-            matcher.push_keystroke(Keystroke::parse("a")?, vec![(1, context_a.clone())]),
-            MatchResult::Matches(vec![(1, Box::new(A("x".to_string())))])
-        );
-        matcher.clear_pending();
-
-        // Multi-keystroke match
-        assert_eq!(
-            matcher.push_keystroke(Keystroke::parse("a")?, vec![(1, context_b.clone())]),
-            MatchResult::Pending
-        );
-        assert_eq!(
-            matcher.push_keystroke(Keystroke::parse("b")?, vec![(1, context_b.clone())]),
-            MatchResult::Matches(vec![(1, Box::new(Ab))])
-        );
-        matcher.clear_pending();
-
-        // Failed matches don't interfere with matching subsequent keys
-        assert_eq!(
-            matcher.push_keystroke(Keystroke::parse("x")?, vec![(1, context_a.clone())]),
-            MatchResult::None
-        );
-        assert_eq!(
-            matcher.push_keystroke(Keystroke::parse("a")?, vec![(1, context_a.clone())]),
-            MatchResult::Matches(vec![(1, Box::new(A("x".to_string())))])
-        );
-        matcher.clear_pending();
-
-        // Pending keystrokes are cleared when the context changes
-        assert_eq!(
-            matcher.push_keystroke(Keystroke::parse("a")?, vec![(1, context_b.clone())]),
-            MatchResult::Pending
-        );
-        assert_eq!(
-            matcher.push_keystroke(Keystroke::parse("b")?, vec![(1, context_a.clone())]),
-            MatchResult::None
-        );
-        matcher.clear_pending();
-
-        let mut context_c = KeymapContext::default();
-        context_c.add_identifier("c");
-
-        // Pending keystrokes are maintained per-view
-        assert_eq!(
-            matcher.push_keystroke(
-                Keystroke::parse("a")?,
-                vec![(1, context_b.clone()), (2, context_c.clone())]
-            ),
-            MatchResult::Pending
-        );
-        assert_eq!(
-            matcher.push_keystroke(Keystroke::parse("b")?, vec![(1, context_b.clone())]),
-            MatchResult::Matches(vec![(1, Box::new(Ab))])
-        );
-
-        // handle Czech $ (option + 4 key)
-        assert_eq!(
-            matcher.push_keystroke(Keystroke::parse("alt-ç->$")?, vec![(1, context_a.clone())]),
-            MatchResult::Matches(vec![(1, Box::new(Dollar))])
-        );
-
-        // handle Brazillian quote (quote key then space key)
-        assert_eq!(
-            matcher.push_keystroke(Keystroke::parse("space->\"")?, vec![(1, context_a.clone())]),
-            MatchResult::Matches(vec![(1, Box::new(Quote))])
-        );
-
-        // handle ctrl+` on a brazillian keyboard
-        assert_eq!(
-            matcher.push_keystroke(Keystroke::parse("ctrl-->`")?, vec![(1, context_a.clone())]),
-            MatchResult::Matches(vec![(1, Box::new(Backtick))])
-        );
-
-        // handle alt-s on a US keyboard
-        assert_eq!(
-            matcher.push_keystroke(Keystroke::parse("alt-s->ß")?, vec![(1, context_a.clone())]),
-            MatchResult::Matches(vec![(1, Box::new(Ess))])
-        );
-
-        Ok(())
-    }
-}

crates/gpui/src/keymap_matcher/binding.rs 🔗

@@ -1,111 +0,0 @@
-use anyhow::Result;
-use smallvec::SmallVec;
-
-use crate::Action;
-
-use super::{KeymapContext, KeymapContextPredicate, Keystroke};
-
-pub struct Binding {
-    action: Box<dyn Action>,
-    pub(super) keystrokes: SmallVec<[Keystroke; 2]>,
-    pub(super) context_predicate: Option<KeymapContextPredicate>,
-}
-
-impl std::fmt::Debug for Binding {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(
-            f,
-            "Binding {{ keystrokes: {:?}, action: {}::{}, context_predicate: {:?} }}",
-            self.keystrokes,
-            self.action.namespace(),
-            self.action.name(),
-            self.context_predicate
-        )
-    }
-}
-
-impl Clone for Binding {
-    fn clone(&self) -> Self {
-        Self {
-            action: self.action.boxed_clone(),
-            keystrokes: self.keystrokes.clone(),
-            context_predicate: self.context_predicate.clone(),
-        }
-    }
-}
-
-impl Binding {
-    pub fn new<A: Action>(keystrokes: &str, action: A, context: Option<&str>) -> Self {
-        Self::load(keystrokes, Box::new(action), context).unwrap()
-    }
-
-    pub fn load(keystrokes: &str, action: Box<dyn Action>, context: Option<&str>) -> Result<Self> {
-        let context = if let Some(context) = context {
-            Some(KeymapContextPredicate::parse(context)?)
-        } else {
-            None
-        };
-
-        let keystrokes = keystrokes
-            .split_whitespace()
-            .map(Keystroke::parse)
-            .collect::<Result<_>>()?;
-
-        Ok(Self {
-            keystrokes,
-            action,
-            context_predicate: context,
-        })
-    }
-
-    pub fn match_context(&self, contexts: &[KeymapContext]) -> bool {
-        self.context_predicate
-            .as_ref()
-            .map(|predicate| predicate.eval(contexts))
-            .unwrap_or(true)
-    }
-
-    pub fn match_keys_and_context(
-        &self,
-        pending_keystrokes: &Vec<Keystroke>,
-        contexts: &[KeymapContext],
-    ) -> BindingMatchResult {
-        if self.keystrokes.as_ref().starts_with(&pending_keystrokes) && self.match_context(contexts)
-        {
-            // If the binding is completed, push it onto the matches list
-            if self.keystrokes.as_ref().len() == pending_keystrokes.len() {
-                BindingMatchResult::Complete(self.action.boxed_clone())
-            } else {
-                BindingMatchResult::Partial
-            }
-        } else {
-            BindingMatchResult::Fail
-        }
-    }
-
-    pub fn keystrokes_for_action(
-        &self,
-        action: &dyn Action,
-        contexts: &[KeymapContext],
-    ) -> Option<SmallVec<[Keystroke; 2]>> {
-        if self.action.eq(action) && self.match_context(contexts) {
-            Some(self.keystrokes.clone())
-        } else {
-            None
-        }
-    }
-
-    pub fn keystrokes(&self) -> &[Keystroke] {
-        self.keystrokes.as_slice()
-    }
-
-    pub fn action(&self) -> &dyn Action {
-        self.action.as_ref()
-    }
-}
-
-pub enum BindingMatchResult {
-    Complete(Box<dyn Action>),
-    Partial,
-    Fail,
-}

crates/gpui/src/keymap_matcher/keymap.rs 🔗

@@ -1,392 +0,0 @@
-use collections::HashSet;
-use smallvec::SmallVec;
-use std::{any::TypeId, collections::HashMap};
-
-use crate::{Action, NoAction};
-
-use super::{Binding, KeymapContextPredicate, Keystroke};
-
-#[derive(Default)]
-pub struct Keymap {
-    bindings: Vec<Binding>,
-    binding_indices_by_action_id: HashMap<TypeId, SmallVec<[usize; 3]>>,
-    disabled_keystrokes: HashMap<SmallVec<[Keystroke; 2]>, HashSet<Option<KeymapContextPredicate>>>,
-}
-
-impl Keymap {
-    #[cfg(test)]
-    pub(super) fn new(bindings: Vec<Binding>) -> Self {
-        let mut this = Self::default();
-        this.add_bindings(bindings);
-        this
-    }
-
-    pub(crate) fn bindings_for_action(
-        &self,
-        action_id: TypeId,
-    ) -> impl Iterator<Item = &'_ Binding> {
-        self.binding_indices_by_action_id
-            .get(&action_id)
-            .map(SmallVec::as_slice)
-            .unwrap_or(&[])
-            .iter()
-            .map(|ix| &self.bindings[*ix])
-            .filter(|binding| !self.binding_disabled(binding))
-    }
-
-    pub(crate) fn add_bindings<T: IntoIterator<Item = Binding>>(&mut self, bindings: T) {
-        let no_action_id = (NoAction {}).id();
-        let mut new_bindings = Vec::new();
-        let mut has_new_disabled_keystrokes = false;
-        for binding in bindings {
-            if binding.action().id() == no_action_id {
-                has_new_disabled_keystrokes |= self
-                    .disabled_keystrokes
-                    .entry(binding.keystrokes)
-                    .or_default()
-                    .insert(binding.context_predicate);
-            } else {
-                new_bindings.push(binding);
-            }
-        }
-
-        if has_new_disabled_keystrokes {
-            self.binding_indices_by_action_id.retain(|_, indices| {
-                indices.retain(|ix| {
-                    let binding = &self.bindings[*ix];
-                    match self.disabled_keystrokes.get(&binding.keystrokes) {
-                        Some(disabled_predicates) => {
-                            !disabled_predicates.contains(&binding.context_predicate)
-                        }
-                        None => true,
-                    }
-                });
-                !indices.is_empty()
-            });
-        }
-
-        for new_binding in new_bindings {
-            if !self.binding_disabled(&new_binding) {
-                self.binding_indices_by_action_id
-                    .entry(new_binding.action().id())
-                    .or_default()
-                    .push(self.bindings.len());
-                self.bindings.push(new_binding);
-            }
-        }
-    }
-
-    pub(crate) fn clear(&mut self) {
-        self.bindings.clear();
-        self.binding_indices_by_action_id.clear();
-        self.disabled_keystrokes.clear();
-    }
-
-    pub fn bindings(&self) -> Vec<&Binding> {
-        self.bindings
-            .iter()
-            .filter(|binding| !self.binding_disabled(binding))
-            .collect()
-    }
-
-    fn binding_disabled(&self, binding: &Binding) -> bool {
-        match self.disabled_keystrokes.get(&binding.keystrokes) {
-            Some(disabled_predicates) => disabled_predicates.contains(&binding.context_predicate),
-            None => false,
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::actions;
-
-    use super::*;
-
-    actions!(
-        keymap_test,
-        [Present1, Present2, Present3, Duplicate, Missing]
-    );
-
-    #[test]
-    fn regular_keymap() {
-        let present_1 = Binding::new("ctrl-q", Present1 {}, None);
-        let present_2 = Binding::new("ctrl-w", Present2 {}, Some("pane"));
-        let present_3 = Binding::new("ctrl-e", Present3 {}, Some("editor"));
-        let keystroke_duplicate_to_1 = Binding::new("ctrl-q", Duplicate {}, None);
-        let full_duplicate_to_2 = Binding::new("ctrl-w", Present2 {}, Some("pane"));
-        let missing = Binding::new("ctrl-r", Missing {}, None);
-        let all_bindings = [
-            &present_1,
-            &present_2,
-            &present_3,
-            &keystroke_duplicate_to_1,
-            &full_duplicate_to_2,
-            &missing,
-        ];
-
-        let mut keymap = Keymap::default();
-        assert_absent(&keymap, &all_bindings);
-        assert!(keymap.bindings().is_empty());
-
-        keymap.add_bindings([present_1.clone(), present_2.clone(), present_3.clone()]);
-        assert_absent(&keymap, &[&keystroke_duplicate_to_1, &missing]);
-        assert_present(
-            &keymap,
-            &[(&present_1, "q"), (&present_2, "w"), (&present_3, "e")],
-        );
-
-        keymap.add_bindings([
-            keystroke_duplicate_to_1.clone(),
-            full_duplicate_to_2.clone(),
-        ]);
-        assert_absent(&keymap, &[&missing]);
-        assert!(
-            !keymap.binding_disabled(&keystroke_duplicate_to_1),
-            "Duplicate binding 1 was added and should not be disabled"
-        );
-        assert!(
-            !keymap.binding_disabled(&full_duplicate_to_2),
-            "Duplicate binding 2 was added and should not be disabled"
-        );
-
-        assert_eq!(
-            keymap
-                .bindings_for_action(keystroke_duplicate_to_1.action().id())
-                .map(|binding| &binding.keystrokes)
-                .flatten()
-                .collect::<Vec<_>>(),
-            vec![&Keystroke {
-                ctrl: true,
-                alt: false,
-                shift: false,
-                cmd: false,
-                function: false,
-                key: "q".to_string(),
-                ime_key: None,
-            }],
-            "{keystroke_duplicate_to_1:?} should have the expected keystroke in the keymap"
-        );
-        assert_eq!(
-            keymap
-                .bindings_for_action(full_duplicate_to_2.action().id())
-                .map(|binding| &binding.keystrokes)
-                .flatten()
-                .collect::<Vec<_>>(),
-            vec![
-                &Keystroke {
-                    ctrl: true,
-                    alt: false,
-                    shift: false,
-                    cmd: false,
-                    function: false,
-                    key: "w".to_string(),
-                    ime_key: None,
-                },
-                &Keystroke {
-                    ctrl: true,
-                    alt: false,
-                    shift: false,
-                    cmd: false,
-                    function: false,
-                    key: "w".to_string(),
-                    ime_key: None,
-                }
-            ],
-            "{full_duplicate_to_2:?} should have a duplicated keystroke in the keymap"
-        );
-
-        let updated_bindings = keymap.bindings();
-        let expected_updated_bindings = vec![
-            &present_1,
-            &present_2,
-            &present_3,
-            &keystroke_duplicate_to_1,
-            &full_duplicate_to_2,
-        ];
-        assert_eq!(
-            updated_bindings.len(),
-            expected_updated_bindings.len(),
-            "Unexpected updated keymap bindings {updated_bindings:?}"
-        );
-        for (i, expected) in expected_updated_bindings.iter().enumerate() {
-            let keymap_binding = &updated_bindings[i];
-            assert_eq!(
-                keymap_binding.context_predicate, expected.context_predicate,
-                "Unexpected context predicate for keymap {i} element: {keymap_binding:?}"
-            );
-            assert_eq!(
-                keymap_binding.keystrokes, expected.keystrokes,
-                "Unexpected keystrokes for keymap {i} element: {keymap_binding:?}"
-            );
-        }
-
-        keymap.clear();
-        assert_absent(&keymap, &all_bindings);
-        assert!(keymap.bindings().is_empty());
-    }
-
-    #[test]
-    fn keymap_with_ignored() {
-        let present_1 = Binding::new("ctrl-q", Present1 {}, None);
-        let present_2 = Binding::new("ctrl-w", Present2 {}, Some("pane"));
-        let present_3 = Binding::new("ctrl-e", Present3 {}, Some("editor"));
-        let keystroke_duplicate_to_1 = Binding::new("ctrl-q", Duplicate {}, None);
-        let full_duplicate_to_2 = Binding::new("ctrl-w", Present2 {}, Some("pane"));
-        let ignored_1 = Binding::new("ctrl-q", NoAction {}, None);
-        let ignored_2 = Binding::new("ctrl-w", NoAction {}, Some("pane"));
-        let ignored_3_with_other_context =
-            Binding::new("ctrl-e", NoAction {}, Some("other_context"));
-
-        let mut keymap = Keymap::default();
-
-        keymap.add_bindings([
-            ignored_1.clone(),
-            ignored_2.clone(),
-            ignored_3_with_other_context.clone(),
-        ]);
-        assert_absent(&keymap, &[&present_3]);
-        assert_disabled(
-            &keymap,
-            &[
-                &present_1,
-                &present_2,
-                &ignored_1,
-                &ignored_2,
-                &ignored_3_with_other_context,
-            ],
-        );
-        assert!(keymap.bindings().is_empty());
-        keymap.clear();
-
-        keymap.add_bindings([
-            present_1.clone(),
-            present_2.clone(),
-            present_3.clone(),
-            ignored_1.clone(),
-            ignored_2.clone(),
-            ignored_3_with_other_context.clone(),
-        ]);
-        assert_present(&keymap, &[(&present_3, "e")]);
-        assert_disabled(
-            &keymap,
-            &[
-                &present_1,
-                &present_2,
-                &ignored_1,
-                &ignored_2,
-                &ignored_3_with_other_context,
-            ],
-        );
-        keymap.clear();
-
-        keymap.add_bindings([
-            present_1.clone(),
-            present_2.clone(),
-            present_3.clone(),
-            ignored_1.clone(),
-        ]);
-        assert_present(&keymap, &[(&present_2, "w"), (&present_3, "e")]);
-        assert_disabled(&keymap, &[&present_1, &ignored_1]);
-        assert_absent(&keymap, &[&ignored_2, &ignored_3_with_other_context]);
-        keymap.clear();
-
-        keymap.add_bindings([
-            present_1.clone(),
-            present_2.clone(),
-            present_3.clone(),
-            keystroke_duplicate_to_1.clone(),
-            full_duplicate_to_2.clone(),
-            ignored_1.clone(),
-            ignored_2.clone(),
-            ignored_3_with_other_context.clone(),
-        ]);
-        assert_present(&keymap, &[(&present_3, "e")]);
-        assert_disabled(
-            &keymap,
-            &[
-                &present_1,
-                &present_2,
-                &keystroke_duplicate_to_1,
-                &full_duplicate_to_2,
-                &ignored_1,
-                &ignored_2,
-                &ignored_3_with_other_context,
-            ],
-        );
-        keymap.clear();
-    }
-
-    #[track_caller]
-    fn assert_present(keymap: &Keymap, expected_bindings: &[(&Binding, &str)]) {
-        let keymap_bindings = keymap.bindings();
-        assert_eq!(
-            expected_bindings.len(),
-            keymap_bindings.len(),
-            "Unexpected keymap bindings {keymap_bindings:?}"
-        );
-        for (i, (expected, expected_key)) in expected_bindings.iter().enumerate() {
-            assert!(
-                !keymap.binding_disabled(expected),
-                "{expected:?} should not be disabled as it was added into keymap for element {i}"
-            );
-            assert_eq!(
-                keymap
-                    .bindings_for_action(expected.action().id())
-                    .map(|binding| &binding.keystrokes)
-                    .flatten()
-                    .collect::<Vec<_>>(),
-                vec![&Keystroke {
-                    ctrl: true,
-                    alt: false,
-                    shift: false,
-                    cmd: false,
-                    function: false,
-                    key: expected_key.to_string(),
-                    ime_key: None,
-                }],
-                "{expected:?} should have the expected keystroke with key '{expected_key}' in the keymap for element {i}"
-            );
-
-            let keymap_binding = &keymap_bindings[i];
-            assert_eq!(
-                keymap_binding.context_predicate, expected.context_predicate,
-                "Unexpected context predicate for keymap {i} element: {keymap_binding:?}"
-            );
-            assert_eq!(
-                keymap_binding.keystrokes, expected.keystrokes,
-                "Unexpected keystrokes for keymap {i} element: {keymap_binding:?}"
-            );
-        }
-    }
-
-    #[track_caller]
-    fn assert_absent(keymap: &Keymap, bindings: &[&Binding]) {
-        for binding in bindings.iter() {
-            assert!(
-                !keymap.binding_disabled(binding),
-                "{binding:?} should not be disabled in the keymap where was not added"
-            );
-            assert_eq!(
-                keymap.bindings_for_action(binding.action().id()).count(),
-                0,
-                "{binding:?} should have no actions in the keymap where was not added"
-            );
-        }
-    }
-
-    #[track_caller]
-    fn assert_disabled(keymap: &Keymap, bindings: &[&Binding]) {
-        for binding in bindings.iter() {
-            assert!(
-                keymap.binding_disabled(binding),
-                "{binding:?} should be disabled in the keymap"
-            );
-            assert_eq!(
-                keymap.bindings_for_action(binding.action().id()).count(),
-                0,
-                "{binding:?} should have no actions in the keymap where it was disabled"
-            );
-        }
-    }
-}

crates/gpui/src/keymap_matcher/keymap_context.rs 🔗

@@ -1,326 +0,0 @@
-use std::borrow::Cow;
-
-use anyhow::{anyhow, Result};
-use collections::{HashMap, HashSet};
-
-#[derive(Clone, Debug, Default, Eq, PartialEq)]
-pub struct KeymapContext {
-    set: HashSet<Cow<'static, str>>,
-    map: HashMap<Cow<'static, str>, Cow<'static, str>>,
-}
-
-impl KeymapContext {
-    pub fn new() -> Self {
-        KeymapContext {
-            set: HashSet::default(),
-            map: HashMap::default(),
-        }
-    }
-
-    pub fn clear(&mut self) {
-        self.set.clear();
-        self.map.clear();
-    }
-
-    pub fn extend(&mut self, other: &Self) {
-        for v in &other.set {
-            self.set.insert(v.clone());
-        }
-        for (k, v) in &other.map {
-            self.map.insert(k.clone(), v.clone());
-        }
-    }
-
-    pub fn add_identifier<I: Into<Cow<'static, str>>>(&mut self, identifier: I) {
-        self.set.insert(identifier.into());
-    }
-
-    pub fn add_key<S1: Into<Cow<'static, str>>, S2: Into<Cow<'static, str>>>(
-        &mut self,
-        key: S1,
-        value: S2,
-    ) {
-        self.map.insert(key.into(), value.into());
-    }
-}
-
-#[derive(Clone, Debug, Eq, PartialEq, Hash)]
-pub enum KeymapContextPredicate {
-    Identifier(String),
-    Equal(String, String),
-    NotEqual(String, String),
-    Child(Box<KeymapContextPredicate>, Box<KeymapContextPredicate>),
-    Not(Box<KeymapContextPredicate>),
-    And(Box<KeymapContextPredicate>, Box<KeymapContextPredicate>),
-    Or(Box<KeymapContextPredicate>, Box<KeymapContextPredicate>),
-}
-
-impl KeymapContextPredicate {
-    pub fn parse(source: &str) -> Result<Self> {
-        let source = Self::skip_whitespace(source);
-        let (predicate, rest) = Self::parse_expr(source, 0)?;
-        if let Some(next) = rest.chars().next() {
-            Err(anyhow!("unexpected character {next:?}"))
-        } else {
-            Ok(predicate)
-        }
-    }
-
-    pub fn eval(&self, contexts: &[KeymapContext]) -> bool {
-        let Some(context) = contexts.first() else {
-            return false;
-        };
-        match self {
-            Self::Identifier(name) => (&context.set).contains(name.as_str()),
-            Self::Equal(left, right) => context
-                .map
-                .get(left.as_str())
-                .map(|value| value == right)
-                .unwrap_or(false),
-            Self::NotEqual(left, right) => context
-                .map
-                .get(left.as_str())
-                .map(|value| value != right)
-                .unwrap_or(true),
-            Self::Not(pred) => !pred.eval(contexts),
-            Self::Child(parent, child) => parent.eval(&contexts[1..]) && child.eval(contexts),
-            Self::And(left, right) => left.eval(contexts) && right.eval(contexts),
-            Self::Or(left, right) => left.eval(contexts) || right.eval(contexts),
-        }
-    }
-
-    fn parse_expr(mut source: &str, min_precedence: u32) -> anyhow::Result<(Self, &str)> {
-        type Op =
-            fn(KeymapContextPredicate, KeymapContextPredicate) -> Result<KeymapContextPredicate>;
-
-        let (mut predicate, rest) = Self::parse_primary(source)?;
-        source = rest;
-
-        'parse: loop {
-            for (operator, precedence, constructor) in [
-                (">", PRECEDENCE_CHILD, Self::new_child as Op),
-                ("&&", PRECEDENCE_AND, Self::new_and as Op),
-                ("||", PRECEDENCE_OR, Self::new_or as Op),
-                ("==", PRECEDENCE_EQ, Self::new_eq as Op),
-                ("!=", PRECEDENCE_EQ, Self::new_neq as Op),
-            ] {
-                if source.starts_with(operator) && precedence >= min_precedence {
-                    source = Self::skip_whitespace(&source[operator.len()..]);
-                    let (right, rest) = Self::parse_expr(source, precedence + 1)?;
-                    predicate = constructor(predicate, right)?;
-                    source = rest;
-                    continue 'parse;
-                }
-            }
-            break;
-        }
-
-        Ok((predicate, source))
-    }
-
-    fn parse_primary(mut source: &str) -> anyhow::Result<(Self, &str)> {
-        let next = source
-            .chars()
-            .next()
-            .ok_or_else(|| anyhow!("unexpected eof"))?;
-        match next {
-            '(' => {
-                source = Self::skip_whitespace(&source[1..]);
-                let (predicate, rest) = Self::parse_expr(source, 0)?;
-                if rest.starts_with(')') {
-                    source = Self::skip_whitespace(&rest[1..]);
-                    Ok((predicate, source))
-                } else {
-                    Err(anyhow!("expected a ')'"))
-                }
-            }
-            '!' => {
-                let source = Self::skip_whitespace(&source[1..]);
-                let (predicate, source) = Self::parse_expr(&source, PRECEDENCE_NOT)?;
-                Ok((KeymapContextPredicate::Not(Box::new(predicate)), source))
-            }
-            _ if next.is_alphanumeric() || next == '_' => {
-                let len = source
-                    .find(|c: char| !(c.is_alphanumeric() || c == '_'))
-                    .unwrap_or(source.len());
-                let (identifier, rest) = source.split_at(len);
-                source = Self::skip_whitespace(rest);
-                Ok((
-                    KeymapContextPredicate::Identifier(identifier.into()),
-                    source,
-                ))
-            }
-            _ => Err(anyhow!("unexpected character {next:?}")),
-        }
-    }
-
-    fn skip_whitespace(source: &str) -> &str {
-        let len = source
-            .find(|c: char| !c.is_whitespace())
-            .unwrap_or(source.len());
-        &source[len..]
-    }
-
-    fn new_or(self, other: Self) -> Result<Self> {
-        Ok(Self::Or(Box::new(self), Box::new(other)))
-    }
-
-    fn new_and(self, other: Self) -> Result<Self> {
-        Ok(Self::And(Box::new(self), Box::new(other)))
-    }
-
-    fn new_child(self, other: Self) -> Result<Self> {
-        Ok(Self::Child(Box::new(self), Box::new(other)))
-    }
-
-    fn new_eq(self, other: Self) -> Result<Self> {
-        if let (Self::Identifier(left), Self::Identifier(right)) = (self, other) {
-            Ok(Self::Equal(left, right))
-        } else {
-            Err(anyhow!("operands must be identifiers"))
-        }
-    }
-
-    fn new_neq(self, other: Self) -> Result<Self> {
-        if let (Self::Identifier(left), Self::Identifier(right)) = (self, other) {
-            Ok(Self::NotEqual(left, right))
-        } else {
-            Err(anyhow!("operands must be identifiers"))
-        }
-    }
-}
-
-const PRECEDENCE_CHILD: u32 = 1;
-const PRECEDENCE_OR: u32 = 2;
-const PRECEDENCE_AND: u32 = 3;
-const PRECEDENCE_EQ: u32 = 4;
-const PRECEDENCE_NOT: u32 = 5;
-
-#[cfg(test)]
-mod tests {
-    use super::KeymapContextPredicate::{self, *};
-
-    #[test]
-    fn test_parse_identifiers() {
-        // Identifiers
-        assert_eq!(
-            KeymapContextPredicate::parse("abc12").unwrap(),
-            Identifier("abc12".into())
-        );
-        assert_eq!(
-            KeymapContextPredicate::parse("_1a").unwrap(),
-            Identifier("_1a".into())
-        );
-    }
-
-    #[test]
-    fn test_parse_negations() {
-        assert_eq!(
-            KeymapContextPredicate::parse("!abc").unwrap(),
-            Not(Box::new(Identifier("abc".into())))
-        );
-        assert_eq!(
-            KeymapContextPredicate::parse(" ! ! abc").unwrap(),
-            Not(Box::new(Not(Box::new(Identifier("abc".into())))))
-        );
-    }
-
-    #[test]
-    fn test_parse_equality_operators() {
-        assert_eq!(
-            KeymapContextPredicate::parse("a == b").unwrap(),
-            Equal("a".into(), "b".into())
-        );
-        assert_eq!(
-            KeymapContextPredicate::parse("c!=d").unwrap(),
-            NotEqual("c".into(), "d".into())
-        );
-        assert_eq!(
-            KeymapContextPredicate::parse("c == !d")
-                .unwrap_err()
-                .to_string(),
-            "operands must be identifiers"
-        );
-    }
-
-    #[test]
-    fn test_parse_boolean_operators() {
-        assert_eq!(
-            KeymapContextPredicate::parse("a || b").unwrap(),
-            Or(
-                Box::new(Identifier("a".into())),
-                Box::new(Identifier("b".into()))
-            )
-        );
-        assert_eq!(
-            KeymapContextPredicate::parse("a || !b && c").unwrap(),
-            Or(
-                Box::new(Identifier("a".into())),
-                Box::new(And(
-                    Box::new(Not(Box::new(Identifier("b".into())))),
-                    Box::new(Identifier("c".into()))
-                ))
-            )
-        );
-        assert_eq!(
-            KeymapContextPredicate::parse("a && b || c&&d").unwrap(),
-            Or(
-                Box::new(And(
-                    Box::new(Identifier("a".into())),
-                    Box::new(Identifier("b".into()))
-                )),
-                Box::new(And(
-                    Box::new(Identifier("c".into())),
-                    Box::new(Identifier("d".into()))
-                ))
-            )
-        );
-        assert_eq!(
-            KeymapContextPredicate::parse("a == b && c || d == e && f").unwrap(),
-            Or(
-                Box::new(And(
-                    Box::new(Equal("a".into(), "b".into())),
-                    Box::new(Identifier("c".into()))
-                )),
-                Box::new(And(
-                    Box::new(Equal("d".into(), "e".into())),
-                    Box::new(Identifier("f".into()))
-                ))
-            )
-        );
-        assert_eq!(
-            KeymapContextPredicate::parse("a && b && c && d").unwrap(),
-            And(
-                Box::new(And(
-                    Box::new(And(
-                        Box::new(Identifier("a".into())),
-                        Box::new(Identifier("b".into()))
-                    )),
-                    Box::new(Identifier("c".into())),
-                )),
-                Box::new(Identifier("d".into()))
-            ),
-        );
-    }
-
-    #[test]
-    fn test_parse_parenthesized_expressions() {
-        assert_eq!(
-            KeymapContextPredicate::parse("a && (b == c || d != e)").unwrap(),
-            And(
-                Box::new(Identifier("a".into())),
-                Box::new(Or(
-                    Box::new(Equal("b".into(), "c".into())),
-                    Box::new(NotEqual("d".into(), "e".into())),
-                )),
-            ),
-        );
-        assert_eq!(
-            KeymapContextPredicate::parse(" ( a || b ) ").unwrap(),
-            Or(
-                Box::new(Identifier("a".into())),
-                Box::new(Identifier("b".into())),
-            )
-        );
-    }
-}

crates/gpui/src/keymap_matcher/keystroke.rs 🔗

@@ -1,141 +0,0 @@
-use std::fmt::Write;
-
-use anyhow::anyhow;
-use serde::Deserialize;
-use smallvec::SmallVec;
-
-#[derive(Clone, Debug, Eq, PartialEq, Default, Deserialize, Hash)]
-pub struct Keystroke {
-    pub ctrl: bool,
-    pub alt: bool,
-    pub shift: bool,
-    pub cmd: bool,
-    pub function: bool,
-    /// key is the character printed on the key that was pressed
-    /// e.g. for option-s, key is "s"
-    pub key: String,
-    /// ime_key is the character inserted by the IME engine when that key was pressed.
-    /// e.g. for option-s, ime_key is "ß"
-    pub ime_key: Option<String>,
-}
-
-impl Keystroke {
-    // When matching a key we cannot know whether the user intended to type
-    // the ime_key or the key. On some non-US keyboards keys we use in our
-    // bindings are behind option (for example `$` is typed `alt-ç` on a Czech keyboard),
-    // and on some keyboards the IME handler converts a sequence of keys into a
-    // specific character (for example `"` is typed as `" space` on a brazillian keyboard).
-    pub fn match_possibilities(&self) -> SmallVec<[Keystroke; 2]> {
-        let mut possibilities = SmallVec::new();
-        match self.ime_key.as_ref() {
-            None => possibilities.push(self.clone()),
-            Some(ime_key) => {
-                possibilities.push(Keystroke {
-                    ctrl: self.ctrl,
-                    alt: false,
-                    shift: false,
-                    cmd: false,
-                    function: false,
-                    key: ime_key.to_string(),
-                    ime_key: None,
-                });
-                possibilities.push(Keystroke {
-                    ime_key: None,
-                    ..self.clone()
-                });
-            }
-        }
-        possibilities
-    }
-
-    /// key syntax is:
-    /// [ctrl-][alt-][shift-][cmd-][fn-]key[->ime_key]
-    /// ime_key is only used for generating test events,
-    /// when matching a key with an ime_key set will be matched without it.
-    pub fn parse(source: &str) -> anyhow::Result<Self> {
-        let mut ctrl = false;
-        let mut alt = false;
-        let mut shift = false;
-        let mut cmd = false;
-        let mut function = false;
-        let mut key = None;
-        let mut ime_key = None;
-
-        let mut components = source.split('-').peekable();
-        while let Some(component) = components.next() {
-            match component {
-                "ctrl" => ctrl = true,
-                "alt" => alt = true,
-                "shift" => shift = true,
-                "cmd" => cmd = true,
-                "fn" => function = true,
-                _ => {
-                    if let Some(next) = components.peek() {
-                        if next.is_empty() && source.ends_with('-') {
-                            key = Some(String::from("-"));
-                            break;
-                        } else if next.len() > 1 && next.starts_with('>') {
-                            key = Some(String::from(component));
-                            ime_key = Some(String::from(&next[1..]));
-                            components.next();
-                        } else {
-                            return Err(anyhow!("Invalid keystroke `{}`", source));
-                        }
-                    } else {
-                        key = Some(String::from(component));
-                    }
-                }
-            }
-        }
-
-        let key = key.ok_or_else(|| anyhow!("Invalid keystroke `{}`", source))?;
-
-        Ok(Keystroke {
-            ctrl,
-            alt,
-            shift,
-            cmd,
-            function,
-            key,
-            ime_key,
-        })
-    }
-
-    pub fn modified(&self) -> bool {
-        self.ctrl || self.alt || self.shift || self.cmd
-    }
-}
-
-impl std::fmt::Display for Keystroke {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        if self.ctrl {
-            f.write_char('^')?;
-        }
-        if self.alt {
-            f.write_char('⌥')?;
-        }
-        if self.cmd {
-            f.write_char('⌘')?;
-        }
-        if self.shift {
-            f.write_char('⇧')?;
-        }
-        let key = match self.key.as_str() {
-            "backspace" => '⌫',
-            "up" => '↑',
-            "down" => '↓',
-            "left" => '←',
-            "right" => '→',
-            "tab" => '⇥',
-            "escape" => '⎋',
-            key => {
-                if key.len() == 1 {
-                    key.chars().next().unwrap().to_ascii_uppercase()
-                } else {
-                    return f.write_str(key);
-                }
-            }
-        };
-        f.write_char(key)
-    }
-}

crates/gpui/src/platform.rs 🔗

@@ -1,37 +1,27 @@
-mod event;
+mod app_menu;
+mod keystroke;
 #[cfg(target_os = "macos")]
-pub mod mac;
-pub mod test;
-pub mod current {
-    #[cfg(target_os = "macos")]
-    pub use super::mac::*;
-}
+mod mac;
+#[cfg(any(test, feature = "test-support"))]
+mod test;
 
 use crate::{
-    executor,
-    fonts::{
-        Features as FontFeatures, FontId, GlyphId, Metrics as FontMetrics,
-        Properties as FontProperties,
-    },
-    geometry::{
-        rect::{RectF, RectI},
-        vector::Vector2F,
-    },
-    keymap_matcher::KeymapMatcher,
-    text_layout::{LineLayout, RunStyle},
-    Action, AnyWindowHandle, ClipboardItem, Menu, Scene,
+    point, size, Action, AnyWindowHandle, BackgroundExecutor, Bounds, DevicePixels, Font, FontId,
+    FontMetrics, FontRun, ForegroundExecutor, GlobalPixels, GlyphId, InputEvent, Keymap,
+    LineLayout, Pixels, Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result,
+    Scene, SharedString, Size, TaskLabel,
 };
-use anyhow::{anyhow, bail, Result};
+use anyhow::{anyhow, bail};
 use async_task::Runnable;
-pub use event::*;
-use pathfinder_geometry::vector::vec2f;
-use postage::oneshot;
-use schemars::JsonSchema;
-use serde::Deserialize;
-use sqlez::{
-    bindable::{Bind, Column, StaticColumnCount},
-    statement::Statement,
-};
+use futures::channel::oneshot;
+use parking::Unparker;
+use seahash::SeaHasher;
+use serde::{Deserialize, Serialize};
+use sqlez::bindable::{Bind, Column, StaticColumnCount};
+use sqlez::statement::Statement;
+use std::borrow::Cow;
+use std::hash::{Hash, Hasher};
+use std::time::Duration;
 use std::{
     any::Any,
     fmt::{self, Debug, Display},
@@ -41,115 +31,126 @@ use std::{
     str::FromStr,
     sync::Arc,
 };
-use time::UtcOffset;
 use uuid::Uuid;
 
-pub trait Platform: Send + Sync {
-    fn dispatcher(&self) -> Arc<dyn Dispatcher>;
-    fn fonts(&self) -> Arc<dyn FontSystem>;
+pub use app_menu::*;
+pub use keystroke::*;
+#[cfg(target_os = "macos")]
+pub use mac::*;
+#[cfg(any(test, feature = "test-support"))]
+pub use test::*;
+pub use time::UtcOffset;
+
+#[cfg(target_os = "macos")]
+pub(crate) fn current_platform() -> Rc<dyn Platform> {
+    Rc::new(MacPlatform::new())
+}
+
+pub type DrawWindow = Box<dyn FnMut() -> Result<Scene>>;
+
+pub(crate) trait Platform: 'static {
+    fn background_executor(&self) -> BackgroundExecutor;
+    fn foreground_executor(&self) -> ForegroundExecutor;
+    fn text_system(&self) -> Arc<dyn PlatformTextSystem>;
 
+    fn run(&self, on_finish_launching: Box<dyn 'static + FnOnce()>);
+    fn quit(&self);
+    fn restart(&self);
     fn activate(&self, ignoring_other_apps: bool);
     fn hide(&self);
     fn hide_other_apps(&self);
     fn unhide_other_apps(&self);
-    fn quit(&self);
-
-    fn screen_by_id(&self, id: Uuid) -> Option<Rc<dyn Screen>>;
-    fn screens(&self) -> Vec<Rc<dyn Screen>>;
 
+    fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>>;
+    fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>>;
+    fn active_window(&self) -> Option<AnyWindowHandle>;
     fn open_window(
         &self,
         handle: AnyWindowHandle,
         options: WindowOptions,
-        executor: Rc<executor::Foreground>,
-    ) -> Box<dyn Window>;
-    fn main_window(&self) -> Option<AnyWindowHandle>;
+        draw: DrawWindow,
+    ) -> Box<dyn PlatformWindow>;
 
-    fn add_status_item(&self, handle: AnyWindowHandle) -> Box<dyn Window>;
+    fn set_display_link_output_callback(
+        &self,
+        display_id: DisplayId,
+        callback: Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp) + Send>,
+    );
+    fn start_display_link(&self, display_id: DisplayId);
+    fn stop_display_link(&self, display_id: DisplayId);
+    // fn add_status_item(&self, _handle: AnyWindowHandle) -> Box<dyn PlatformWindow>;
 
-    fn write_to_clipboard(&self, item: ClipboardItem);
-    fn read_from_clipboard(&self) -> Option<ClipboardItem>;
     fn open_url(&self, url: &str);
+    fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>);
+    fn prompt_for_paths(
+        &self,
+        options: PathPromptOptions,
+    ) -> oneshot::Receiver<Option<Vec<PathBuf>>>;
+    fn prompt_for_new_path(&self, directory: &Path) -> oneshot::Receiver<Option<PathBuf>>;
+    fn reveal_path(&self, path: &Path);
 
-    fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Result<()>;
-    fn read_credentials(&self, url: &str) -> Result<Option<(String, Vec<u8>)>>;
-    fn delete_credentials(&self, url: &str) -> Result<()>;
+    fn on_become_active(&self, callback: Box<dyn FnMut()>);
+    fn on_resign_active(&self, callback: Box<dyn FnMut()>);
+    fn on_quit(&self, callback: Box<dyn FnMut()>);
+    fn on_reopen(&self, callback: Box<dyn FnMut()>);
+    fn on_event(&self, callback: Box<dyn FnMut(InputEvent) -> bool>);
 
-    fn set_cursor_style(&self, style: CursorStyle);
-    fn should_auto_hide_scrollbars(&self) -> bool;
+    fn set_menus(&self, menus: Vec<Menu>, keymap: &Keymap);
+    fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>);
+    fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>);
+    fn on_validate_app_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>);
 
+    fn os_name(&self) -> &'static str;
+    fn os_version(&self) -> Result<SemanticVersion>;
+    fn app_version(&self) -> Result<SemanticVersion>;
+    fn app_path(&self) -> Result<PathBuf>;
     fn local_timezone(&self) -> UtcOffset;
-
+    fn double_click_interval(&self) -> Duration;
     fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf>;
-    fn app_path(&self) -> Result<PathBuf>;
-    fn app_version(&self) -> Result<AppVersion>;
-    fn os_name(&self) -> &'static str;
-    fn os_version(&self) -> Result<AppVersion>;
-    fn restart(&self);
-}
-
-pub(crate) trait ForegroundPlatform {
-    fn on_become_active(&self, callback: Box<dyn FnMut()>);
-    fn on_resign_active(&self, callback: Box<dyn FnMut()>);
-    fn on_quit(&self, callback: Box<dyn FnMut()>);
 
-    /// Handle the application being re-activated with no windows open.
-    fn on_reopen(&self, callback: Box<dyn FnMut()>);
+    fn set_cursor_style(&self, style: CursorStyle);
+    fn should_auto_hide_scrollbars(&self) -> bool;
 
-    fn on_event(&self, callback: Box<dyn FnMut(Event) -> bool>);
-    fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>);
-    fn run(&self, on_finish_launching: Box<dyn FnOnce()>);
+    fn write_to_clipboard(&self, item: ClipboardItem);
+    fn read_from_clipboard(&self) -> Option<ClipboardItem>;
 
-    fn on_menu_command(&self, callback: Box<dyn FnMut(&dyn Action)>);
-    fn on_validate_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>);
-    fn on_will_open_menu(&self, callback: Box<dyn FnMut()>);
-    fn set_menus(&self, menus: Vec<Menu>, matcher: &KeymapMatcher);
-    fn prompt_for_paths(
-        &self,
-        options: PathPromptOptions,
-    ) -> oneshot::Receiver<Option<Vec<PathBuf>>>;
-    fn prompt_for_new_path(&self, directory: &Path) -> oneshot::Receiver<Option<PathBuf>>;
-    fn reveal_path(&self, path: &Path);
+    fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Result<()>;
+    fn read_credentials(&self, url: &str) -> Result<Option<(String, Vec<u8>)>>;
+    fn delete_credentials(&self, url: &str) -> Result<()>;
 }
 
-pub trait Dispatcher: Send + Sync {
-    fn is_main_thread(&self) -> bool;
-    fn run_on_main_thread(&self, task: Runnable);
+pub trait PlatformDisplay: Send + Sync + Debug {
+    fn id(&self) -> DisplayId;
+    /// Returns a stable identifier for this display that can be persisted and used
+    /// across system restarts.
+    fn uuid(&self) -> Result<Uuid>;
+    fn as_any(&self) -> &dyn Any;
+    fn bounds(&self) -> Bounds<GlobalPixels>;
 }
 
-pub trait InputHandler {
-    fn selected_text_range(&self) -> Option<Range<usize>>;
-    fn marked_text_range(&self) -> Option<Range<usize>>;
-    fn text_for_range(&self, range_utf16: Range<usize>) -> Option<String>;
-    fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str);
-    fn replace_and_mark_text_in_range(
-        &mut self,
-        range_utf16: Option<Range<usize>>,
-        new_text: &str,
-        new_selected_range: Option<Range<usize>>,
-    );
-    fn unmark_text(&mut self);
-    fn rect_for_range(&self, range_utf16: Range<usize>) -> Option<RectF>;
-}
+#[derive(PartialEq, Eq, Hash, Copy, Clone)]
+pub struct DisplayId(pub(crate) u32);
 
-pub trait Screen: Debug {
-    fn as_any(&self) -> &dyn Any;
-    fn bounds(&self) -> RectF;
-    fn content_bounds(&self) -> RectF;
-    fn display_uuid(&self) -> Option<Uuid>;
+impl Debug for DisplayId {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "DisplayId({})", self.0)
+    }
 }
 
-pub trait Window {
+unsafe impl Send for DisplayId {}
+
+pub trait PlatformWindow {
     fn bounds(&self) -> WindowBounds;
-    fn content_size(&self) -> Vector2F;
+    fn content_size(&self) -> Size<Pixels>;
     fn scale_factor(&self) -> f32;
-    fn titlebar_height(&self) -> f32;
-    fn appearance(&self) -> Appearance;
-    fn screen(&self) -> Rc<dyn Screen>;
-    fn mouse_position(&self) -> Vector2F;
-
+    fn titlebar_height(&self) -> Pixels;
+    fn appearance(&self) -> WindowAppearance;
+    fn display(&self) -> Rc<dyn PlatformDisplay>;
+    fn mouse_position(&self) -> Point<Pixels>;
+    fn modifiers(&self) -> Modifiers;
     fn as_any_mut(&mut self) -> &mut dyn Any;
-    fn set_input_handler(&mut self, input_handler: Box<dyn InputHandler>);
+    fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>);
+    fn clear_input_handler(&mut self);
     fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver<usize>;
     fn activate(&self);
     fn set_title(&mut self, title: &str);
@@ -157,47 +158,214 @@ pub trait Window {
     fn show_character_palette(&self);
     fn minimize(&self);
     fn zoom(&self);
-    fn present_scene(&mut self, scene: Scene);
     fn toggle_full_screen(&self);
+    fn on_input(&self, callback: Box<dyn FnMut(InputEvent) -> bool>);
+    fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>);
+    fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>);
+    fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>);
+    fn on_moved(&self, callback: Box<dyn FnMut()>);
+    fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>);
+    fn on_close(&self, callback: Box<dyn FnOnce()>);
+    fn on_appearance_changed(&self, callback: Box<dyn FnMut()>);
+    fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool;
+    fn invalidate(&self);
+
+    fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas>;
+
+    #[cfg(any(test, feature = "test-support"))]
+    fn as_test(&mut self) -> Option<&mut TestWindow> {
+        None
+    }
+}
+
+pub trait PlatformDispatcher: Send + Sync {
+    fn is_main_thread(&self) -> bool;
+    fn dispatch(&self, runnable: Runnable, label: Option<TaskLabel>);
+    fn dispatch_on_main_thread(&self, runnable: Runnable);
+    fn dispatch_after(&self, duration: Duration, runnable: Runnable);
+    fn tick(&self, background_only: bool) -> bool;
+    fn park(&self);
+    fn unparker(&self) -> Unparker;
+
+    #[cfg(any(test, feature = "test-support"))]
+    fn as_test(&self) -> Option<&TestDispatcher> {
+        None
+    }
+}
 
-    fn on_event(&mut self, callback: Box<dyn FnMut(Event) -> bool>);
-    fn on_active_status_change(&mut self, callback: Box<dyn FnMut(bool)>);
-    fn on_resize(&mut self, callback: Box<dyn FnMut()>);
-    fn on_fullscreen(&mut self, callback: Box<dyn FnMut(bool)>);
-    fn on_moved(&mut self, callback: Box<dyn FnMut()>);
-    fn on_should_close(&mut self, callback: Box<dyn FnMut() -> bool>);
-    fn on_close(&mut self, callback: Box<dyn FnOnce()>);
-    fn on_appearance_changed(&mut self, callback: Box<dyn FnMut()>);
-    fn is_topmost_for_position(&self, position: Vector2F) -> bool;
+pub trait PlatformTextSystem: Send + Sync {
+    fn add_fonts(&self, fonts: &[Arc<Vec<u8>>]) -> Result<()>;
+    fn all_font_families(&self) -> Vec<String>;
+    fn font_id(&self, descriptor: &Font) -> Result<FontId>;
+    fn font_metrics(&self, font_id: FontId) -> FontMetrics;
+    fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>>;
+    fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Size<f32>>;
+    fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId>;
+    fn glyph_raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>>;
+    fn rasterize_glyph(
+        &self,
+        params: &RenderGlyphParams,
+        raster_bounds: Bounds<DevicePixels>,
+    ) -> Result<(Size<DevicePixels>, Vec<u8>)>;
+    fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> LineLayout;
+    fn wrap_line(
+        &self,
+        text: &str,
+        font_id: FontId,
+        font_size: Pixels,
+        width: Pixels,
+    ) -> Vec<usize>;
+}
+
+#[derive(Clone, Debug)]
+pub struct AppMetadata {
+    pub os_name: &'static str,
+    pub os_version: Option<SemanticVersion>,
+    pub app_version: Option<SemanticVersion>,
+}
+
+#[derive(PartialEq, Eq, Hash, Clone)]
+pub enum AtlasKey {
+    Glyph(RenderGlyphParams),
+    Svg(RenderSvgParams),
+    Image(RenderImageParams),
+}
+
+impl AtlasKey {
+    pub(crate) fn texture_kind(&self) -> AtlasTextureKind {
+        match self {
+            AtlasKey::Glyph(params) => {
+                if params.is_emoji {
+                    AtlasTextureKind::Polychrome
+                } else {
+                    AtlasTextureKind::Monochrome
+                }
+            }
+            AtlasKey::Svg(_) => AtlasTextureKind::Monochrome,
+            AtlasKey::Image(_) => AtlasTextureKind::Polychrome,
+        }
+    }
+}
+
+impl From<RenderGlyphParams> for AtlasKey {
+    fn from(params: RenderGlyphParams) -> Self {
+        Self::Glyph(params)
+    }
+}
+
+impl From<RenderSvgParams> for AtlasKey {
+    fn from(params: RenderSvgParams) -> Self {
+        Self::Svg(params)
+    }
+}
+
+impl From<RenderImageParams> for AtlasKey {
+    fn from(params: RenderImageParams) -> Self {
+        Self::Image(params)
+    }
+}
+
+pub trait PlatformAtlas: Send + Sync {
+    fn get_or_insert_with<'a>(
+        &self,
+        key: &AtlasKey,
+        build: &mut dyn FnMut() -> Result<(Size<DevicePixels>, Cow<'a, [u8]>)>,
+    ) -> Result<AtlasTile>;
+
+    fn clear(&self);
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+#[repr(C)]
+pub struct AtlasTile {
+    pub(crate) texture_id: AtlasTextureId,
+    pub(crate) tile_id: TileId,
+    pub(crate) bounds: Bounds<DevicePixels>,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+#[repr(C)]
+pub(crate) struct AtlasTextureId {
+    // We use u32 instead of usize for Metal Shader Language compatibility
+    pub(crate) index: u32,
+    pub(crate) kind: AtlasTextureKind,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+#[repr(C)]
+pub(crate) enum AtlasTextureKind {
+    Monochrome = 0,
+    Polychrome = 1,
+    Path = 2,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
+#[repr(C)]
+pub(crate) struct TileId(pub(crate) u32);
+
+impl From<etagere::AllocId> for TileId {
+    fn from(id: etagere::AllocId) -> Self {
+        Self(id.serialize())
+    }
+}
+
+impl From<TileId> for etagere::AllocId {
+    fn from(id: TileId) -> Self {
+        Self::deserialize(id.0)
+    }
+}
+
+pub trait PlatformInputHandler: 'static {
+    fn selected_text_range(&mut self) -> Option<Range<usize>>;
+    fn marked_text_range(&mut self) -> Option<Range<usize>>;
+    fn text_for_range(&mut self, range_utf16: Range<usize>) -> Option<String>;
+    fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str);
+    fn replace_and_mark_text_in_range(
+        &mut self,
+        range_utf16: Option<Range<usize>>,
+        new_text: &str,
+        new_selected_range: Option<Range<usize>>,
+    );
+    fn unmark_text(&mut self);
+    fn bounds_for_range(&mut self, range_utf16: Range<usize>) -> Option<Bounds<Pixels>>;
 }
 
 #[derive(Debug)]
-pub struct WindowOptions<'a> {
+pub struct WindowOptions {
     pub bounds: WindowBounds,
-    pub titlebar: Option<TitlebarOptions<'a>>,
+    pub titlebar: Option<TitlebarOptions>,
     pub center: bool,
     pub focus: bool,
     pub show: bool,
     pub kind: WindowKind,
     pub is_movable: bool,
-    pub screen: Option<Rc<dyn Screen>>,
+    pub display_id: Option<DisplayId>,
 }
 
-impl<'a> WindowOptions<'a> {
-    pub fn with_bounds(bounds: Vector2F) -> Self {
+impl Default for WindowOptions {
+    fn default() -> Self {
         Self {
-            bounds: WindowBounds::Fixed(RectF::new(vec2f(0., 0.), bounds)),
-            center: true,
-            ..Default::default()
+            bounds: WindowBounds::default(),
+            titlebar: Some(TitlebarOptions {
+                title: Default::default(),
+                appears_transparent: Default::default(),
+                traffic_light_position: Default::default(),
+            }),
+            center: false,
+            focus: true,
+            show: true,
+            kind: WindowKind::Normal,
+            is_movable: true,
+            display_id: None,
         }
     }
 }
 
 #[derive(Debug, Default)]
-pub struct TitlebarOptions<'a> {
-    pub title: Option<&'a str>,
+pub struct TitlebarOptions {
+    pub title: Option<SharedString>,
     pub appears_transparent: bool,
-    pub traffic_light_position: Option<Vector2F>,
+    pub traffic_light_position: Option<Point<Pixels>>,
 }
 
 #[derive(Copy, Clone, Debug)]
@@ -220,11 +388,12 @@ pub enum WindowKind {
     PopUp,
 }
 
-#[derive(Copy, Clone, Debug, PartialEq)]
+#[derive(Copy, Clone, Debug, PartialEq, Default)]
 pub enum WindowBounds {
     Fullscreen,
+    #[default]
     Maximized,
-    Fixed(RectF),
+    Fixed(Bounds<GlobalPixels>),
 }
 
 impl StaticColumnCount for WindowBounds {
@@ -253,10 +422,10 @@ impl Bind for WindowBounds {
         statement.bind(
             &region.map(|region| {
                 (
-                    region.min_x(),
-                    region.min_y(),
-                    region.width(),
-                    region.height(),
+                    region.origin.x,
+                    region.origin.y,
+                    region.size.width,
+                    region.size.height,
                 )
             }),
             next_index,
@@ -272,10 +441,14 @@ impl Column for WindowBounds {
             "Maximized" => WindowBounds::Maximized,
             "Fixed" => {
                 let ((x, y, width, height), _) = Column::column(statement, next_index)?;
-                WindowBounds::Fixed(RectF::new(
-                    Vector2F::new(x, y),
-                    Vector2F::new(width, height),
-                ))
+                let x: f64 = x;
+                let y: f64 = y;
+                let width: f64 = width;
+                let height: f64 = height;
+                WindowBounds::Fixed(Bounds {
+                    origin: point(x.into(), y.into()),
+                    size: size(width.into(), height.into()),
+                })
             }
             _ => bail!("Window State did not have a valid string"),
         };
@@ -284,25 +457,55 @@ impl Column for WindowBounds {
     }
 }
 
+#[derive(Copy, Clone, Debug)]
+pub enum WindowAppearance {
+    Light,
+    VibrantLight,
+    Dark,
+    VibrantDark,
+}
+
+impl Default for WindowAppearance {
+    fn default() -> Self {
+        Self::Light
+    }
+}
+
+#[derive(Copy, Clone, Debug)]
 pub struct PathPromptOptions {
     pub files: bool,
     pub directories: bool,
     pub multiple: bool,
 }
 
+#[derive(Copy, Clone, Debug)]
 pub enum PromptLevel {
     Info,
     Warning,
     Critical,
 }
 
-#[derive(Copy, Clone, Debug, Deserialize, JsonSchema)]
+/// The style of the cursor (pointer)
+#[derive(Copy, Clone, Debug)]
 pub enum CursorStyle {
     Arrow,
+    IBeam,
+    Crosshair,
+    ClosedHand,
+    OpenHand,
+    PointingHand,
+    ResizeLeft,
+    ResizeRight,
     ResizeLeftRight,
+    ResizeUp,
+    ResizeDown,
     ResizeUpDown,
-    PointingHand,
-    IBeam,
+    DisappearingItem,
+    IBeamCursorForVerticalLayout,
+    OperationNotAllowed,
+    DragLink,
+    DragCopy,
+    ContextualMenu,
 }
 
 impl Default for CursorStyle {
@@ -311,14 +514,14 @@ impl Default for CursorStyle {
     }
 }
 
-#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
-pub struct AppVersion {
+#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Serialize)]
+pub struct SemanticVersion {
     major: usize,
     minor: usize,
     patch: usize,
 }
 
-impl FromStr for AppVersion {
+impl FromStr for SemanticVersion {
     type Err = anyhow::Error;
 
     fn from_str(s: &str) -> Result<Self> {
@@ -343,59 +546,47 @@ impl FromStr for AppVersion {
     }
 }
 
-impl Display for AppVersion {
+impl Display for SemanticVersion {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
     }
 }
 
-#[derive(Copy, Clone, Debug)]
-pub enum RasterizationOptions {
-    Alpha,
-    Bgra,
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct ClipboardItem {
+    pub(crate) text: String,
+    pub(crate) metadata: Option<String>,
 }
 
-pub trait FontSystem: Send + Sync {
-    fn add_fonts(&self, fonts: &[Arc<Vec<u8>>]) -> anyhow::Result<()>;
-    fn all_families(&self) -> Vec<String>;
-    fn load_family(&self, name: &str, features: &FontFeatures) -> anyhow::Result<Vec<FontId>>;
-    fn select_font(
-        &self,
-        font_ids: &[FontId],
-        properties: &FontProperties,
-    ) -> anyhow::Result<FontId>;
-    fn font_metrics(&self, font_id: FontId) -> FontMetrics;
-    fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result<RectF>;
-    fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result<Vector2F>;
-    fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId>;
-    fn rasterize_glyph(
-        &self,
-        font_id: FontId,
-        font_size: f32,
-        glyph_id: GlyphId,
-        subpixel_shift: Vector2F,
-        scale_factor: f32,
-        options: RasterizationOptions,
-    ) -> Option<(RectI, Vec<u8>)>;
-    fn layout_line(&self, text: &str, font_size: f32, runs: &[(usize, RunStyle)]) -> LineLayout;
-    fn wrap_line(&self, text: &str, font_id: FontId, font_size: f32, width: f32) -> Vec<usize>;
-}
-
-impl<'a> Default for WindowOptions<'a> {
-    fn default() -> Self {
+impl ClipboardItem {
+    pub fn new(text: String) -> Self {
         Self {
-            bounds: WindowBounds::Maximized,
-            titlebar: Some(TitlebarOptions {
-                title: Default::default(),
-                appears_transparent: Default::default(),
-                traffic_light_position: Default::default(),
-            }),
-            center: false,
-            focus: true,
-            show: true,
-            kind: WindowKind::Normal,
-            is_movable: true,
-            screen: None,
+            text,
+            metadata: None,
         }
     }
+
+    pub fn with_metadata<T: Serialize>(mut self, metadata: T) -> Self {
+        self.metadata = Some(serde_json::to_string(&metadata).unwrap());
+        self
+    }
+
+    pub fn text(&self) -> &String {
+        &self.text
+    }
+
+    pub fn metadata<T>(&self) -> Option<T>
+    where
+        T: for<'a> Deserialize<'a>,
+    {
+        self.metadata
+            .as_ref()
+            .and_then(|m| serde_json::from_str(m).ok())
+    }
+
+    pub(crate) fn text_hash(text: &str) -> u64 {
+        let mut hasher = SeaHasher::new();
+        text.hash(&mut hasher);
+        hasher.finish()
+    }
 }

crates/gpui/src/platform/event.rs 🔗

@@ -1,236 +0,0 @@
-use std::{any::Any, ops::Deref};
-
-use pathfinder_geometry::vector::vec2f;
-
-use crate::{geometry::vector::Vector2F, keymap_matcher::Keystroke};
-
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct KeyDownEvent {
-    pub keystroke: Keystroke,
-    pub is_held: bool,
-}
-
-#[derive(Clone, Debug)]
-pub struct KeyUpEvent {
-    pub keystroke: Keystroke,
-}
-
-#[derive(Clone, Copy, Debug, Default, PartialEq)]
-pub struct Modifiers {
-    pub ctrl: bool,
-    pub alt: bool,
-    pub shift: bool,
-    pub cmd: bool,
-    pub fun: bool,
-}
-
-#[derive(Clone, Copy, Debug, Default)]
-pub struct ModifiersChangedEvent {
-    pub modifiers: Modifiers,
-}
-
-impl Deref for ModifiersChangedEvent {
-    type Target = Modifiers;
-
-    fn deref(&self) -> &Self::Target {
-        &self.modifiers
-    }
-}
-
-/// The phase of a touch motion event.
-/// Based on the winit enum of the same name,
-#[derive(Clone, Copy, Debug)]
-pub enum TouchPhase {
-    Started,
-    Moved,
-    Ended,
-}
-
-#[derive(Clone, Copy, Debug)]
-pub enum ScrollDelta {
-    Pixels(Vector2F),
-    Lines(Vector2F),
-}
-
-impl Default for ScrollDelta {
-    fn default() -> Self {
-        Self::Lines(Default::default())
-    }
-}
-
-impl ScrollDelta {
-    pub fn raw(&self) -> &Vector2F {
-        match self {
-            ScrollDelta::Pixels(v) => v,
-            ScrollDelta::Lines(v) => v,
-        }
-    }
-
-    pub fn precise(&self) -> bool {
-        match self {
-            ScrollDelta::Pixels(_) => true,
-            ScrollDelta::Lines(_) => false,
-        }
-    }
-
-    pub fn pixel_delta(&self, line_height: f32) -> Vector2F {
-        match self {
-            ScrollDelta::Pixels(delta) => *delta,
-            ScrollDelta::Lines(delta) => vec2f(delta.x() * line_height, delta.y() * line_height),
-        }
-    }
-}
-
-#[derive(Clone, Copy, Debug, Default)]
-pub struct ScrollWheelEvent {
-    pub position: Vector2F,
-    pub delta: ScrollDelta,
-    pub modifiers: Modifiers,
-    /// If the platform supports returning the phase of a scroll wheel event, it will be stored here
-    pub phase: Option<TouchPhase>,
-}
-
-impl Deref for ScrollWheelEvent {
-    type Target = Modifiers;
-
-    fn deref(&self) -> &Self::Target {
-        &self.modifiers
-    }
-}
-
-#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
-pub enum NavigationDirection {
-    Back,
-    Forward,
-}
-
-impl Default for NavigationDirection {
-    fn default() -> Self {
-        Self::Back
-    }
-}
-
-#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
-pub enum MouseButton {
-    Left,
-    Right,
-    Middle,
-    Navigate(NavigationDirection),
-}
-
-impl MouseButton {
-    pub fn all() -> Vec<Self> {
-        vec![
-            MouseButton::Left,
-            MouseButton::Right,
-            MouseButton::Middle,
-            MouseButton::Navigate(NavigationDirection::Back),
-            MouseButton::Navigate(NavigationDirection::Forward),
-        ]
-    }
-}
-
-impl Default for MouseButton {
-    fn default() -> Self {
-        Self::Left
-    }
-}
-
-#[derive(Clone, Copy, Debug, Default)]
-pub struct MouseButtonEvent {
-    pub button: MouseButton,
-    pub position: Vector2F,
-    pub modifiers: Modifiers,
-    pub click_count: usize,
-    pub is_down: bool,
-}
-
-impl Deref for MouseButtonEvent {
-    type Target = Modifiers;
-
-    fn deref(&self) -> &Self::Target {
-        &self.modifiers
-    }
-}
-
-#[derive(Clone, Copy, Debug, Default)]
-pub struct MouseMovedEvent {
-    pub position: Vector2F,
-    pub pressed_button: Option<MouseButton>,
-    pub modifiers: Modifiers,
-}
-
-impl Deref for MouseMovedEvent {
-    type Target = Modifiers;
-
-    fn deref(&self) -> &Self::Target {
-        &self.modifiers
-    }
-}
-
-impl MouseMovedEvent {
-    pub fn to_button_event(&self, button: MouseButton) -> MouseButtonEvent {
-        MouseButtonEvent {
-            position: self.position,
-            button: self.pressed_button.unwrap_or(button),
-            modifiers: self.modifiers,
-            click_count: 0,
-            is_down: self.pressed_button.is_some(),
-        }
-    }
-}
-
-#[derive(Clone, Copy, Debug, Default)]
-pub struct MouseExitedEvent {
-    pub position: Vector2F,
-    pub pressed_button: Option<MouseButton>,
-    pub modifiers: Modifiers,
-}
-
-impl Deref for MouseExitedEvent {
-    type Target = Modifiers;
-
-    fn deref(&self) -> &Self::Target {
-        &self.modifiers
-    }
-}
-
-#[derive(Clone, Debug)]
-pub enum Event {
-    KeyDown(KeyDownEvent),
-    KeyUp(KeyUpEvent),
-    ModifiersChanged(ModifiersChangedEvent),
-    MouseDown(MouseButtonEvent),
-    MouseUp(MouseButtonEvent),
-    MouseMoved(MouseMovedEvent),
-    MouseExited(MouseExitedEvent),
-    ScrollWheel(ScrollWheelEvent),
-}
-
-impl Event {
-    pub fn position(&self) -> Option<Vector2F> {
-        match self {
-            Event::KeyDown { .. } => None,
-            Event::KeyUp { .. } => None,
-            Event::ModifiersChanged { .. } => None,
-            Event::MouseDown(event) => Some(event.position),
-            Event::MouseUp(event) => Some(event.position),
-            Event::MouseMoved(event) => Some(event.position),
-            Event::MouseExited(event) => Some(event.position),
-            Event::ScrollWheel(event) => Some(event.position),
-        }
-    }
-
-    pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> {
-        match self {
-            Event::KeyDown { .. } => None,
-            Event::KeyUp { .. } => None,
-            Event::ModifiersChanged { .. } => None,
-            Event::MouseDown(event) => Some(event),
-            Event::MouseUp(event) => Some(event),
-            Event::MouseMoved(event) => Some(event),
-            Event::MouseExited(event) => Some(event),
-            Event::ScrollWheel(event) => Some(event),
-        }
-    }
-}

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

@@ -1,39 +1,33 @@
-mod appearance;
-mod atlas;
+//! Macos screen have a y axis that goings up from the bottom of the screen and
+//! an origin at the bottom left of the main display.
 mod dispatcher;
-mod event;
-mod fonts;
-mod geometry;
-mod image_cache;
+mod display;
+mod display_linker;
+mod events;
+mod metal_atlas;
+mod metal_renderer;
+mod open_type;
 mod platform;
-mod renderer;
-mod screen;
-mod sprite_cache;
-mod status_item;
+mod text_system;
 mod window;
+mod window_appearence;
 
+use crate::{px, size, GlobalPixels, Pixels, Size};
 use cocoa::{
-    base::{id, nil, BOOL, NO, YES},
-    foundation::{NSAutoreleasePool, NSNotFound, NSString, NSUInteger},
+    base::{id, nil},
+    foundation::{NSAutoreleasePool, NSNotFound, NSRect, NSSize, NSString, NSUInteger},
 };
-pub use dispatcher::Dispatcher;
-pub use fonts::FontSystem;
-use platform::{MacForegroundPlatform, MacPlatform};
-pub use renderer::Surface;
-use std::{ops::Range, rc::Rc, sync::Arc};
-use window::MacWindow;
+use metal_renderer::*;
+use objc::runtime::{BOOL, NO, YES};
+use std::ops::Range;
 
-use crate::executor;
-
-pub fn platform() -> Arc<dyn super::Platform> {
-    Arc::new(MacPlatform::new())
-}
-
-pub(crate) fn foreground_platform(
-    foreground: Rc<executor::Foreground>,
-) -> Rc<dyn super::ForegroundPlatform> {
-    Rc::new(MacForegroundPlatform::new(foreground))
-}
+pub use dispatcher::*;
+pub use display::*;
+pub use display_linker::*;
+pub use metal_atlas::*;
+pub use platform::*;
+pub use text_system::*;
+pub use window::*;
 
 trait BoolExt {
     fn to_objc(self) -> BOOL;
@@ -102,3 +96,44 @@ unsafe impl objc::Encode for NSRange {
 unsafe fn ns_string(string: &str) -> id {
     NSString::alloc(nil).init_str(string).autorelease()
 }
+
+impl From<NSSize> for Size<Pixels> {
+    fn from(value: NSSize) -> Self {
+        Size {
+            width: px(value.width as f32),
+            height: px(value.height as f32),
+        }
+    }
+}
+
+pub trait NSRectExt {
+    fn size(&self) -> Size<Pixels>;
+    fn intersects(&self, other: Self) -> bool;
+}
+
+impl From<NSRect> for Size<Pixels> {
+    fn from(rect: NSRect) -> Self {
+        let NSSize { width, height } = rect.size;
+        size(width.into(), height.into())
+    }
+}
+
+impl From<NSRect> for Size<GlobalPixels> {
+    fn from(rect: NSRect) -> Self {
+        let NSSize { width, height } = rect.size;
+        size(width.into(), height.into())
+    }
+}
+
+// impl NSRectExt for NSRect {
+//     fn intersects(&self, other: Self) -> bool {
+//         self.size.width > 0.
+//             && self.size.height > 0.
+//             && other.size.width > 0.
+//             && other.size.height > 0.
+//             && self.origin.x <= other.origin.x + other.size.width
+//             && self.origin.x + self.size.width >= other.origin.x
+//             && self.origin.y <= other.origin.y + other.size.height
+//             && self.origin.y + self.size.height >= other.origin.y
+//     }
+// }

crates/gpui/src/platform/mac/appearance.rs 🔗

@@ -1,36 +0,0 @@
-use std::ffi::CStr;
-
-use crate::platform::Appearance;
-use cocoa::{
-    appkit::{NSAppearanceNameVibrantDark, NSAppearanceNameVibrantLight},
-    base::id,
-    foundation::NSString,
-};
-use objc::{msg_send, sel, sel_impl};
-
-impl Appearance {
-    pub unsafe fn from_native(appearance: id) -> Self {
-        let name: id = msg_send![appearance, name];
-        if name == NSAppearanceNameVibrantLight {
-            Self::VibrantLight
-        } else if name == NSAppearanceNameVibrantDark {
-            Self::VibrantDark
-        } else if name == NSAppearanceNameAqua {
-            Self::Light
-        } else if name == NSAppearanceNameDarkAqua {
-            Self::Dark
-        } else {
-            println!(
-                "unknown appearance: {:?}",
-                CStr::from_ptr(name.UTF8String())
-            );
-            Self::Light
-        }
-    }
-}
-
-#[link(name = "AppKit", kind = "framework")]
-extern "C" {
-    pub static NSAppearanceNameAqua: id;
-    pub static NSAppearanceNameDarkAqua: id;
-}

crates/gpui/src/platform/mac/atlas.rs 🔗

@@ -1,173 +0,0 @@
-use crate::geometry::{
-    rect::RectI,
-    vector::{vec2i, Vector2I},
-};
-use etagere::BucketedAtlasAllocator;
-use foreign_types::ForeignType;
-use log::warn;
-use metal::{Device, TextureDescriptor};
-use objc::{msg_send, sel, sel_impl};
-
-pub struct AtlasAllocator {
-    device: Device,
-    texture_descriptor: TextureDescriptor,
-    atlases: Vec<Atlas>,
-    last_used_atlas_id: usize,
-}
-
-#[derive(Copy, Clone, Debug)]
-pub struct AllocId {
-    pub atlas_id: usize,
-    alloc_id: etagere::AllocId,
-}
-
-impl AtlasAllocator {
-    pub fn new(device: Device, texture_descriptor: TextureDescriptor) -> Self {
-        let mut this = Self {
-            device,
-            texture_descriptor,
-            atlases: vec![],
-            last_used_atlas_id: 0,
-        };
-        let atlas = this.new_atlas(Vector2I::zero());
-        this.atlases.push(atlas);
-        this
-    }
-
-    pub fn default_atlas_size(&self) -> Vector2I {
-        vec2i(
-            self.texture_descriptor.width() as i32,
-            self.texture_descriptor.height() as i32,
-        )
-    }
-
-    pub fn allocate(&mut self, requested_size: Vector2I) -> Option<(AllocId, Vector2I)> {
-        let atlas_id = self.last_used_atlas_id;
-        if let Some((alloc_id, origin)) = self.atlases[atlas_id].allocate(requested_size) {
-            return Some((AllocId { atlas_id, alloc_id }, origin));
-        }
-
-        for (atlas_id, atlas) in self.atlases.iter_mut().enumerate() {
-            if atlas_id == self.last_used_atlas_id {
-                continue;
-            }
-            if let Some((alloc_id, origin)) = atlas.allocate(requested_size) {
-                self.last_used_atlas_id = atlas_id;
-                return Some((AllocId { atlas_id, alloc_id }, origin));
-            }
-        }
-
-        let atlas_id = self.atlases.len();
-        let mut atlas = self.new_atlas(requested_size);
-        let allocation = atlas
-            .allocate(requested_size)
-            .map(|(alloc_id, origin)| (AllocId { atlas_id, alloc_id }, origin));
-        self.atlases.push(atlas);
-
-        if allocation.is_none() {
-            warn!(
-                "allocation of size {:?} could not be created",
-                requested_size,
-            );
-        }
-
-        allocation
-    }
-
-    pub fn upload(&mut self, size: Vector2I, bytes: &[u8]) -> Option<(AllocId, RectI)> {
-        let (alloc_id, origin) = self.allocate(size)?;
-        let bounds = RectI::new(origin, size);
-        self.atlases[alloc_id.atlas_id].upload(bounds, bytes);
-        Some((alloc_id, bounds))
-    }
-
-    pub fn deallocate(&mut self, id: AllocId) {
-        if let Some(atlas) = self.atlases.get_mut(id.atlas_id) {
-            atlas.deallocate(id.alloc_id);
-        }
-    }
-
-    pub fn clear(&mut self) {
-        for atlas in &mut self.atlases {
-            atlas.clear();
-        }
-    }
-
-    pub fn texture(&self, atlas_id: usize) -> Option<&metal::TextureRef> {
-        self.atlases.get(atlas_id).map(|a| a.texture.as_ref())
-    }
-
-    fn new_atlas(&mut self, required_size: Vector2I) -> Atlas {
-        let size = self.default_atlas_size().max(required_size);
-        let texture = if size.x() as u64 > self.texture_descriptor.width()
-            || size.y() as u64 > self.texture_descriptor.height()
-        {
-            let descriptor = unsafe {
-                let descriptor_ptr: *mut metal::MTLTextureDescriptor =
-                    msg_send![self.texture_descriptor, copy];
-                metal::TextureDescriptor::from_ptr(descriptor_ptr)
-            };
-            descriptor.set_width(size.x() as u64);
-            descriptor.set_height(size.y() as u64);
-
-            self.device.new_texture(&descriptor)
-        } else {
-            self.device.new_texture(&self.texture_descriptor)
-        };
-        Atlas::new(size, texture)
-    }
-}
-
-struct Atlas {
-    allocator: BucketedAtlasAllocator,
-    texture: metal::Texture,
-}
-
-impl Atlas {
-    fn new(size: Vector2I, texture: metal::Texture) -> Self {
-        Self {
-            allocator: BucketedAtlasAllocator::new(etagere::Size::new(size.x(), size.y())),
-            texture,
-        }
-    }
-
-    fn allocate(&mut self, size: Vector2I) -> Option<(etagere::AllocId, Vector2I)> {
-        let alloc = self
-            .allocator
-            .allocate(etagere::Size::new(size.x(), size.y()))?;
-        let origin = alloc.rectangle.min;
-        Some((alloc.id, vec2i(origin.x, origin.y)))
-    }
-
-    fn upload(&mut self, bounds: RectI, bytes: &[u8]) {
-        let region = metal::MTLRegion::new_2d(
-            bounds.origin().x() as u64,
-            bounds.origin().y() as u64,
-            bounds.size().x() as u64,
-            bounds.size().y() as u64,
-        );
-        self.texture.replace_region(
-            region,
-            0,
-            bytes.as_ptr() as *const _,
-            (bounds.size().x() * self.bytes_per_pixel() as i32) as u64,
-        );
-    }
-
-    fn bytes_per_pixel(&self) -> u8 {
-        use metal::MTLPixelFormat::*;
-        match self.texture.pixel_format() {
-            A8Unorm | R8Unorm => 1,
-            RGBA8Unorm | BGRA8Unorm => 4,
-            _ => unimplemented!(),
-        }
-    }
-
-    fn deallocate(&mut self, id: etagere::AllocId) {
-        self.allocator.deallocate(id);
-    }
-
-    fn clear(&mut self) {
-        self.allocator.clear();
-    }
-}

crates/gpui/src/platform/mac/dispatcher.rs 🔗

@@ -2,15 +2,16 @@
 #![allow(non_camel_case_types)]
 #![allow(non_snake_case)]
 
+use crate::{PlatformDispatcher, TaskLabel};
 use async_task::Runnable;
 use objc::{
     class, msg_send,
     runtime::{BOOL, YES},
     sel, sel_impl,
 };
-use std::ffi::c_void;
-
-use crate::platform;
+use parking::{Parker, Unparker};
+use parking_lot::Mutex;
+use std::{ffi::c_void, sync::Arc, time::Duration};
 
 include!(concat!(env!("OUT_DIR"), "/dispatch_sys.rs"));
 
@@ -18,15 +19,41 @@ pub fn dispatch_get_main_queue() -> dispatch_queue_t {
     unsafe { &_dispatch_main_q as *const _ as dispatch_queue_t }
 }
 
-pub struct Dispatcher;
+pub struct MacDispatcher {
+    parker: Arc<Mutex<Parker>>,
+}
+
+impl Default for MacDispatcher {
+    fn default() -> Self {
+        Self::new()
+    }
+}
 
-impl platform::Dispatcher for Dispatcher {
+impl MacDispatcher {
+    pub fn new() -> Self {
+        MacDispatcher {
+            parker: Arc::new(Mutex::new(Parker::new())),
+        }
+    }
+}
+
+impl PlatformDispatcher for MacDispatcher {
     fn is_main_thread(&self) -> bool {
         let is_main_thread: BOOL = unsafe { msg_send![class!(NSThread), isMainThread] };
         is_main_thread == YES
     }
 
-    fn run_on_main_thread(&self, runnable: Runnable) {
+    fn dispatch(&self, runnable: Runnable, _: Option<TaskLabel>) {
+        unsafe {
+            dispatch_async_f(
+                dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT.try_into().unwrap(), 0),
+                runnable.into_raw() as *mut c_void,
+                Some(trampoline),
+            );
+        }
+    }
+
+    fn dispatch_on_main_thread(&self, runnable: Runnable) {
         unsafe {
             dispatch_async_f(
                 dispatch_get_main_queue(),
@@ -34,10 +61,36 @@ impl platform::Dispatcher for Dispatcher {
                 Some(trampoline),
             );
         }
+    }
 
-        extern "C" fn trampoline(runnable: *mut c_void) {
-            let task = unsafe { Runnable::from_raw(runnable as *mut ()) };
-            task.run();
+    fn dispatch_after(&self, duration: Duration, runnable: Runnable) {
+        unsafe {
+            let queue =
+                dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT.try_into().unwrap(), 0);
+            let when = dispatch_time(DISPATCH_TIME_NOW as u64, duration.as_nanos() as i64);
+            dispatch_after_f(
+                when,
+                queue,
+                runnable.into_raw() as *mut c_void,
+                Some(trampoline),
+            );
         }
     }
+
+    fn tick(&self, _background_only: bool) -> bool {
+        false
+    }
+
+    fn park(&self) {
+        self.parker.lock().park()
+    }
+
+    fn unparker(&self) -> Unparker {
+        self.parker.lock().unparker()
+    }
+}
+
+extern "C" fn trampoline(runnable: *mut c_void) {
+    let task = unsafe { Runnable::from_raw(runnable as *mut ()) };
+    task.run();
 }

crates/gpui/src/platform/mac/event.rs 🔗

@@ -1,359 +0,0 @@
-use crate::{
-    geometry::vector::vec2f,
-    keymap_matcher::Keystroke,
-    platform::{
-        Event, KeyDownEvent, KeyUpEvent, Modifiers, ModifiersChangedEvent, MouseButton,
-        MouseButtonEvent, MouseExitedEvent, MouseMovedEvent, NavigationDirection, ScrollDelta,
-        ScrollWheelEvent, TouchPhase,
-    },
-};
-use cocoa::{
-    appkit::{NSEvent, NSEventModifierFlags, NSEventPhase, NSEventType},
-    base::{id, YES},
-    foundation::NSString as _,
-};
-use core_graphics::{
-    event::{CGEvent, CGEventFlags, CGKeyCode},
-    event_source::{CGEventSource, CGEventSourceStateID},
-};
-use ctor::ctor;
-use foreign_types::ForeignType;
-use objc::{class, msg_send, sel, sel_impl};
-use std::{borrow::Cow, ffi::CStr, mem, os::raw::c_char, ptr};
-
-const BACKSPACE_KEY: u16 = 0x7f;
-const SPACE_KEY: u16 = b' ' as u16;
-const ENTER_KEY: u16 = 0x0d;
-const NUMPAD_ENTER_KEY: u16 = 0x03;
-const ESCAPE_KEY: u16 = 0x1b;
-const TAB_KEY: u16 = 0x09;
-const SHIFT_TAB_KEY: u16 = 0x19;
-
-static mut EVENT_SOURCE: core_graphics::sys::CGEventSourceRef = ptr::null_mut();
-
-#[ctor]
-unsafe fn build_event_source() {
-    let source = CGEventSource::new(CGEventSourceStateID::Private).unwrap();
-    EVENT_SOURCE = source.as_ptr();
-    mem::forget(source);
-}
-
-pub fn key_to_native(key: &str) -> Cow<str> {
-    use cocoa::appkit::*;
-    let code = match key {
-        "space" => SPACE_KEY,
-        "backspace" => BACKSPACE_KEY,
-        "up" => NSUpArrowFunctionKey,
-        "down" => NSDownArrowFunctionKey,
-        "left" => NSLeftArrowFunctionKey,
-        "right" => NSRightArrowFunctionKey,
-        "pageup" => NSPageUpFunctionKey,
-        "pagedown" => NSPageDownFunctionKey,
-        "home" => NSHomeFunctionKey,
-        "end" => NSEndFunctionKey,
-        "delete" => NSDeleteFunctionKey,
-        "f1" => NSF1FunctionKey,
-        "f2" => NSF2FunctionKey,
-        "f3" => NSF3FunctionKey,
-        "f4" => NSF4FunctionKey,
-        "f5" => NSF5FunctionKey,
-        "f6" => NSF6FunctionKey,
-        "f7" => NSF7FunctionKey,
-        "f8" => NSF8FunctionKey,
-        "f9" => NSF9FunctionKey,
-        "f10" => NSF10FunctionKey,
-        "f11" => NSF11FunctionKey,
-        "f12" => NSF12FunctionKey,
-        _ => return Cow::Borrowed(key),
-    };
-    Cow::Owned(String::from_utf16(&[code]).unwrap())
-}
-
-unsafe fn read_modifiers(native_event: id) -> Modifiers {
-    let modifiers = native_event.modifierFlags();
-    let ctrl = modifiers.contains(NSEventModifierFlags::NSControlKeyMask);
-    let alt = modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask);
-    let shift = modifiers.contains(NSEventModifierFlags::NSShiftKeyMask);
-    let cmd = modifiers.contains(NSEventModifierFlags::NSCommandKeyMask);
-    let fun = modifiers.contains(NSEventModifierFlags::NSFunctionKeyMask);
-
-    Modifiers {
-        ctrl,
-        alt,
-        shift,
-        cmd,
-        fun,
-    }
-}
-
-impl Event {
-    pub unsafe fn from_native(native_event: id, window_height: Option<f32>) -> Option<Self> {
-        let event_type = native_event.eventType();
-
-        // Filter out event types that aren't in the NSEventType enum.
-        // See https://github.com/servo/cocoa-rs/issues/155#issuecomment-323482792 for details.
-        match event_type as u64 {
-            0 | 21 | 32 | 33 | 35 | 36 | 37 => {
-                return None;
-            }
-            _ => {}
-        }
-
-        match event_type {
-            NSEventType::NSFlagsChanged => Some(Self::ModifiersChanged(ModifiersChangedEvent {
-                modifiers: read_modifiers(native_event),
-            })),
-            NSEventType::NSKeyDown => Some(Self::KeyDown(KeyDownEvent {
-                keystroke: parse_keystroke(native_event),
-                is_held: native_event.isARepeat() == YES,
-            })),
-            NSEventType::NSKeyUp => Some(Self::KeyUp(KeyUpEvent {
-                keystroke: parse_keystroke(native_event),
-            })),
-            NSEventType::NSLeftMouseDown
-            | NSEventType::NSRightMouseDown
-            | NSEventType::NSOtherMouseDown => {
-                let button = match native_event.buttonNumber() {
-                    0 => MouseButton::Left,
-                    1 => MouseButton::Right,
-                    2 => MouseButton::Middle,
-                    3 => MouseButton::Navigate(NavigationDirection::Back),
-                    4 => MouseButton::Navigate(NavigationDirection::Forward),
-                    // Other mouse buttons aren't tracked currently
-                    _ => return None,
-                };
-                window_height.map(|window_height| {
-                    Self::MouseDown(MouseButtonEvent {
-                        button,
-                        position: vec2f(
-                            native_event.locationInWindow().x as f32,
-                            // MacOS screen coordinates are relative to bottom left
-                            window_height - native_event.locationInWindow().y as f32,
-                        ),
-                        modifiers: read_modifiers(native_event),
-                        click_count: native_event.clickCount() as usize,
-                        is_down: true,
-                    })
-                })
-            }
-            NSEventType::NSLeftMouseUp
-            | NSEventType::NSRightMouseUp
-            | NSEventType::NSOtherMouseUp => {
-                let button = match native_event.buttonNumber() {
-                    0 => MouseButton::Left,
-                    1 => MouseButton::Right,
-                    2 => MouseButton::Middle,
-                    3 => MouseButton::Navigate(NavigationDirection::Back),
-                    4 => MouseButton::Navigate(NavigationDirection::Forward),
-                    // Other mouse buttons aren't tracked currently
-                    _ => return None,
-                };
-
-                window_height.map(|window_height| {
-                    Self::MouseUp(MouseButtonEvent {
-                        button,
-                        position: vec2f(
-                            native_event.locationInWindow().x as f32,
-                            // MacOS view coordinates are relative to bottom left
-                            window_height - native_event.locationInWindow().y as f32,
-                        ),
-                        modifiers: read_modifiers(native_event),
-                        click_count: native_event.clickCount() as usize,
-                        is_down: false,
-                    })
-                })
-            }
-            NSEventType::NSScrollWheel => window_height.map(|window_height| {
-                let phase = match native_event.phase() {
-                    NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => {
-                        Some(TouchPhase::Started)
-                    }
-                    NSEventPhase::NSEventPhaseEnded => Some(TouchPhase::Ended),
-                    _ => Some(TouchPhase::Moved),
-                };
-
-                let raw_data = vec2f(
-                    native_event.scrollingDeltaX() as f32,
-                    native_event.scrollingDeltaY() as f32,
-                );
-
-                let delta = if native_event.hasPreciseScrollingDeltas() == YES {
-                    ScrollDelta::Pixels(raw_data)
-                } else {
-                    ScrollDelta::Lines(raw_data)
-                };
-
-                Self::ScrollWheel(ScrollWheelEvent {
-                    position: vec2f(
-                        native_event.locationInWindow().x as f32,
-                        window_height - native_event.locationInWindow().y as f32,
-                    ),
-                    delta,
-                    phase,
-                    modifiers: read_modifiers(native_event),
-                })
-            }),
-            NSEventType::NSLeftMouseDragged
-            | NSEventType::NSRightMouseDragged
-            | NSEventType::NSOtherMouseDragged => {
-                let pressed_button = match native_event.buttonNumber() {
-                    0 => MouseButton::Left,
-                    1 => MouseButton::Right,
-                    2 => MouseButton::Middle,
-                    3 => MouseButton::Navigate(NavigationDirection::Back),
-                    4 => MouseButton::Navigate(NavigationDirection::Forward),
-                    // Other mouse buttons aren't tracked currently
-                    _ => return None,
-                };
-
-                window_height.map(|window_height| {
-                    Self::MouseMoved(MouseMovedEvent {
-                        pressed_button: Some(pressed_button),
-                        position: vec2f(
-                            native_event.locationInWindow().x as f32,
-                            window_height - native_event.locationInWindow().y as f32,
-                        ),
-                        modifiers: read_modifiers(native_event),
-                    })
-                })
-            }
-            NSEventType::NSMouseMoved => window_height.map(|window_height| {
-                Self::MouseMoved(MouseMovedEvent {
-                    position: vec2f(
-                        native_event.locationInWindow().x as f32,
-                        window_height - native_event.locationInWindow().y as f32,
-                    ),
-                    pressed_button: None,
-                    modifiers: read_modifiers(native_event),
-                })
-            }),
-            NSEventType::NSMouseExited => window_height.map(|window_height| {
-                Self::MouseExited(MouseExitedEvent {
-                    position: vec2f(
-                        native_event.locationInWindow().x as f32,
-                        window_height - native_event.locationInWindow().y as f32,
-                    ),
-                    pressed_button: None,
-                    modifiers: read_modifiers(native_event),
-                })
-            }),
-            _ => None,
-        }
-    }
-}
-
-unsafe fn parse_keystroke(native_event: id) -> Keystroke {
-    use cocoa::appkit::*;
-
-    let mut chars_ignoring_modifiers =
-        CStr::from_ptr(native_event.charactersIgnoringModifiers().UTF8String() as *mut c_char)
-            .to_str()
-            .unwrap()
-            .to_string();
-    let first_char = chars_ignoring_modifiers.chars().next().map(|ch| ch as u16);
-    let modifiers = native_event.modifierFlags();
-
-    let ctrl = modifiers.contains(NSEventModifierFlags::NSControlKeyMask);
-    let alt = modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask);
-    let mut shift = modifiers.contains(NSEventModifierFlags::NSShiftKeyMask);
-    let cmd = modifiers.contains(NSEventModifierFlags::NSCommandKeyMask);
-    let function = modifiers.contains(NSEventModifierFlags::NSFunctionKeyMask)
-        && first_char.map_or(true, |ch| {
-            !(NSUpArrowFunctionKey..=NSModeSwitchFunctionKey).contains(&ch)
-        });
-
-    #[allow(non_upper_case_globals)]
-    let key = match first_char {
-        Some(SPACE_KEY) => "space".to_string(),
-        Some(BACKSPACE_KEY) => "backspace".to_string(),
-        Some(ENTER_KEY) | Some(NUMPAD_ENTER_KEY) => "enter".to_string(),
-        Some(ESCAPE_KEY) => "escape".to_string(),
-        Some(TAB_KEY) => "tab".to_string(),
-        Some(SHIFT_TAB_KEY) => "tab".to_string(),
-        Some(NSUpArrowFunctionKey) => "up".to_string(),
-        Some(NSDownArrowFunctionKey) => "down".to_string(),
-        Some(NSLeftArrowFunctionKey) => "left".to_string(),
-        Some(NSRightArrowFunctionKey) => "right".to_string(),
-        Some(NSPageUpFunctionKey) => "pageup".to_string(),
-        Some(NSPageDownFunctionKey) => "pagedown".to_string(),
-        Some(NSHomeFunctionKey) => "home".to_string(),
-        Some(NSEndFunctionKey) => "end".to_string(),
-        Some(NSDeleteFunctionKey) => "delete".to_string(),
-        Some(NSF1FunctionKey) => "f1".to_string(),
-        Some(NSF2FunctionKey) => "f2".to_string(),
-        Some(NSF3FunctionKey) => "f3".to_string(),
-        Some(NSF4FunctionKey) => "f4".to_string(),
-        Some(NSF5FunctionKey) => "f5".to_string(),
-        Some(NSF6FunctionKey) => "f6".to_string(),
-        Some(NSF7FunctionKey) => "f7".to_string(),
-        Some(NSF8FunctionKey) => "f8".to_string(),
-        Some(NSF9FunctionKey) => "f9".to_string(),
-        Some(NSF10FunctionKey) => "f10".to_string(),
-        Some(NSF11FunctionKey) => "f11".to_string(),
-        Some(NSF12FunctionKey) => "f12".to_string(),
-        _ => {
-            let mut chars_ignoring_modifiers_and_shift =
-                chars_for_modified_key(native_event.keyCode(), false, false);
-
-            // Honor ⌘ when Dvorak-QWERTY is used.
-            let chars_with_cmd = chars_for_modified_key(native_event.keyCode(), true, false);
-            if cmd && chars_ignoring_modifiers_and_shift != chars_with_cmd {
-                chars_ignoring_modifiers =
-                    chars_for_modified_key(native_event.keyCode(), true, shift);
-                chars_ignoring_modifiers_and_shift = chars_with_cmd;
-            }
-
-            if shift {
-                if chars_ignoring_modifiers_and_shift
-                    == chars_ignoring_modifiers.to_ascii_lowercase()
-                {
-                    chars_ignoring_modifiers_and_shift
-                } else if chars_ignoring_modifiers_and_shift != chars_ignoring_modifiers {
-                    shift = false;
-                    chars_ignoring_modifiers
-                } else {
-                    chars_ignoring_modifiers
-                }
-            } else {
-                chars_ignoring_modifiers
-            }
-        }
-    };
-
-    Keystroke {
-        ctrl,
-        alt,
-        shift,
-        cmd,
-        function,
-        key,
-        ime_key: None,
-    }
-}
-
-fn chars_for_modified_key(code: CGKeyCode, cmd: bool, shift: bool) -> String {
-    // Ideally, we would use `[NSEvent charactersByApplyingModifiers]` but that
-    // always returns an empty string with certain keyboards, e.g. Japanese. Synthesizing
-    // an event with the given flags instead lets us access `characters`, which always
-    // returns a valid string.
-    let source = unsafe { core_graphics::event_source::CGEventSource::from_ptr(EVENT_SOURCE) };
-    let event = CGEvent::new_keyboard_event(source.clone(), code, true).unwrap();
-    mem::forget(source);
-
-    let mut flags = CGEventFlags::empty();
-    if cmd {
-        flags |= CGEventFlags::CGEventFlagCommand;
-    }
-    if shift {
-        flags |= CGEventFlags::CGEventFlagShift;
-    }
-    event.set_flags(flags);
-
-    unsafe {
-        let event: id = msg_send![class!(NSEvent), eventWithCGEvent: &*event];
-        CStr::from_ptr(event.characters().UTF8String())
-            .to_str()
-            .unwrap()
-            .to_string()
-    }
-}

crates/gpui/src/platform/mac/fonts.rs 🔗

@@ -1,671 +0,0 @@
-mod open_type;
-
-use crate::{
-    fonts::{Features, FontId, GlyphId, Metrics, Properties},
-    geometry::{
-        rect::{RectF, RectI},
-        transform2d::Transform2F,
-        vector::{vec2f, Vector2F},
-    },
-    platform::{self, RasterizationOptions},
-    text_layout::{Glyph, LineLayout, Run, RunStyle},
-};
-use cocoa::appkit::{CGFloat, CGPoint};
-use collections::HashMap;
-use core_foundation::{
-    array::CFIndex,
-    attributed_string::{CFAttributedStringRef, CFMutableAttributedString},
-    base::{CFRange, TCFType},
-    string::CFString,
-};
-use core_graphics::{
-    base::{kCGImageAlphaPremultipliedLast, CGGlyph},
-    color_space::CGColorSpace,
-    context::CGContext,
-};
-use core_text::{font::CTFont, line::CTLine, string_attributes::kCTFontAttributeName};
-use font_kit::{
-    handle::Handle, hinting::HintingOptions, source::SystemSource, sources::mem::MemSource,
-};
-use parking_lot::RwLock;
-use std::{cell::RefCell, char, cmp, convert::TryFrom, ffi::c_void, sync::Arc};
-
-#[allow(non_upper_case_globals)]
-const kCGImageAlphaOnly: u32 = 7;
-
-pub struct FontSystem(RwLock<FontSystemState>);
-
-struct FontSystemState {
-    memory_source: MemSource,
-    system_source: SystemSource,
-    fonts: Vec<font_kit::font::Font>,
-    font_ids_by_postscript_name: HashMap<String, FontId>,
-    postscript_names_by_font_id: HashMap<FontId, String>,
-}
-
-impl FontSystem {
-    pub fn new() -> Self {
-        Self(RwLock::new(FontSystemState {
-            memory_source: MemSource::empty(),
-            system_source: SystemSource::new(),
-            fonts: Vec::new(),
-            font_ids_by_postscript_name: Default::default(),
-            postscript_names_by_font_id: Default::default(),
-        }))
-    }
-}
-
-impl Default for FontSystem {
-    fn default() -> Self {
-        Self::new()
-    }
-}
-
-impl platform::FontSystem for FontSystem {
-    fn add_fonts(&self, fonts: &[Arc<Vec<u8>>]) -> anyhow::Result<()> {
-        self.0.write().add_fonts(fonts)
-    }
-
-    fn all_families(&self) -> Vec<String> {
-        self.0
-            .read()
-            .system_source
-            .all_families()
-            .expect("core text should never return an error")
-    }
-
-    fn load_family(&self, name: &str, features: &Features) -> anyhow::Result<Vec<FontId>> {
-        self.0.write().load_family(name, features)
-    }
-
-    fn select_font(&self, font_ids: &[FontId], properties: &Properties) -> anyhow::Result<FontId> {
-        self.0.read().select_font(font_ids, properties)
-    }
-
-    fn font_metrics(&self, font_id: FontId) -> Metrics {
-        self.0.read().font_metrics(font_id)
-    }
-
-    fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result<RectF> {
-        self.0.read().typographic_bounds(font_id, glyph_id)
-    }
-
-    fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result<Vector2F> {
-        self.0.read().advance(font_id, glyph_id)
-    }
-
-    fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId> {
-        self.0.read().glyph_for_char(font_id, ch)
-    }
-
-    fn rasterize_glyph(
-        &self,
-        font_id: FontId,
-        font_size: f32,
-        glyph_id: GlyphId,
-        subpixel_shift: Vector2F,
-        scale_factor: f32,
-        options: RasterizationOptions,
-    ) -> Option<(RectI, Vec<u8>)> {
-        self.0.read().rasterize_glyph(
-            font_id,
-            font_size,
-            glyph_id,
-            subpixel_shift,
-            scale_factor,
-            options,
-        )
-    }
-
-    fn layout_line(&self, text: &str, font_size: f32, runs: &[(usize, RunStyle)]) -> LineLayout {
-        self.0.write().layout_line(text, font_size, runs)
-    }
-
-    fn wrap_line(&self, text: &str, font_id: FontId, font_size: f32, width: f32) -> Vec<usize> {
-        self.0.read().wrap_line(text, font_id, font_size, width)
-    }
-}
-
-impl FontSystemState {
-    fn add_fonts(&mut self, fonts: &[Arc<Vec<u8>>]) -> anyhow::Result<()> {
-        self.memory_source.add_fonts(
-            fonts
-                .iter()
-                .map(|bytes| Handle::from_memory(bytes.clone(), 0)),
-        )?;
-        Ok(())
-    }
-
-    fn load_family(&mut self, name: &str, features: &Features) -> anyhow::Result<Vec<FontId>> {
-        let mut font_ids = Vec::new();
-
-        let family = self
-            .memory_source
-            .select_family_by_name(name)
-            .or_else(|_| self.system_source.select_family_by_name(name))?;
-        for font in family.fonts() {
-            let mut font = font.load()?;
-            open_type::apply_features(&mut font, features);
-            let font_id = FontId(self.fonts.len());
-            font_ids.push(font_id);
-            let postscript_name = font.postscript_name().unwrap();
-            self.font_ids_by_postscript_name
-                .insert(postscript_name.clone(), font_id);
-            self.postscript_names_by_font_id
-                .insert(font_id, postscript_name);
-            self.fonts.push(font);
-        }
-        Ok(font_ids)
-    }
-
-    fn select_font(&self, font_ids: &[FontId], properties: &Properties) -> anyhow::Result<FontId> {
-        let candidates = font_ids
-            .iter()
-            .map(|font_id| self.fonts[font_id.0].properties())
-            .collect::<Vec<_>>();
-        let idx = font_kit::matching::find_best_match(&candidates, properties)?;
-        Ok(font_ids[idx])
-    }
-
-    fn font_metrics(&self, font_id: FontId) -> Metrics {
-        self.fonts[font_id.0].metrics()
-    }
-
-    fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result<RectF> {
-        Ok(self.fonts[font_id.0].typographic_bounds(glyph_id)?)
-    }
-
-    fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result<Vector2F> {
-        Ok(self.fonts[font_id.0].advance(glyph_id)?)
-    }
-
-    fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId> {
-        self.fonts[font_id.0].glyph_for_char(ch)
-    }
-
-    fn id_for_native_font(&mut self, requested_font: CTFont) -> FontId {
-        let postscript_name = requested_font.postscript_name();
-        if let Some(font_id) = self.font_ids_by_postscript_name.get(&postscript_name) {
-            *font_id
-        } else {
-            let font_id = FontId(self.fonts.len());
-            self.font_ids_by_postscript_name
-                .insert(postscript_name.clone(), font_id);
-            self.postscript_names_by_font_id
-                .insert(font_id, postscript_name);
-            self.fonts
-                .push(font_kit::font::Font::from_core_graphics_font(
-                    requested_font.copy_to_CGFont(),
-                ));
-            font_id
-        }
-    }
-
-    fn is_emoji(&self, font_id: FontId) -> bool {
-        self.postscript_names_by_font_id
-            .get(&font_id)
-            .map_or(false, |postscript_name| {
-                postscript_name == "AppleColorEmoji"
-            })
-    }
-
-    fn rasterize_glyph(
-        &self,
-        font_id: FontId,
-        font_size: f32,
-        glyph_id: GlyphId,
-        subpixel_shift: Vector2F,
-        scale_factor: f32,
-        options: RasterizationOptions,
-    ) -> Option<(RectI, Vec<u8>)> {
-        let font = &self.fonts[font_id.0];
-        let scale = Transform2F::from_scale(scale_factor);
-        let glyph_bounds = font
-            .raster_bounds(
-                glyph_id,
-                font_size,
-                scale,
-                HintingOptions::None,
-                font_kit::canvas::RasterizationOptions::GrayscaleAa,
-            )
-            .ok()?;
-
-        if glyph_bounds.width() == 0 || glyph_bounds.height() == 0 {
-            None
-        } else {
-            // Make room for subpixel variants.
-            let subpixel_padding = subpixel_shift.ceil().to_i32();
-            let cx_bounds = RectI::new(
-                glyph_bounds.origin(),
-                glyph_bounds.size() + subpixel_padding,
-            );
-
-            let mut bytes;
-            let cx;
-            match options {
-                RasterizationOptions::Alpha => {
-                    bytes = vec![0; cx_bounds.width() as usize * cx_bounds.height() as usize];
-                    cx = CGContext::create_bitmap_context(
-                        Some(bytes.as_mut_ptr() as *mut _),
-                        cx_bounds.width() as usize,
-                        cx_bounds.height() as usize,
-                        8,
-                        cx_bounds.width() as usize,
-                        &CGColorSpace::create_device_gray(),
-                        kCGImageAlphaOnly,
-                    );
-                }
-                RasterizationOptions::Bgra => {
-                    bytes = vec![0; cx_bounds.width() as usize * 4 * cx_bounds.height() as usize];
-                    cx = CGContext::create_bitmap_context(
-                        Some(bytes.as_mut_ptr() as *mut _),
-                        cx_bounds.width() as usize,
-                        cx_bounds.height() as usize,
-                        8,
-                        cx_bounds.width() as usize * 4,
-                        &CGColorSpace::create_device_rgb(),
-                        kCGImageAlphaPremultipliedLast,
-                    );
-                }
-            }
-
-            // Move the origin to bottom left and account for scaling, this
-            // makes drawing text consistent with the font-kit's raster_bounds.
-            cx.translate(
-                -glyph_bounds.origin_x() as CGFloat,
-                (glyph_bounds.origin_y() + glyph_bounds.height()) as CGFloat,
-            );
-            cx.scale(scale_factor as CGFloat, scale_factor as CGFloat);
-
-            cx.set_allows_font_subpixel_positioning(true);
-            cx.set_should_subpixel_position_fonts(true);
-            cx.set_allows_font_subpixel_quantization(false);
-            cx.set_should_subpixel_quantize_fonts(false);
-            font.native_font()
-                .clone_with_font_size(font_size as CGFloat)
-                .draw_glyphs(
-                    &[glyph_id as CGGlyph],
-                    &[CGPoint::new(
-                        (subpixel_shift.x() / scale_factor) as CGFloat,
-                        (subpixel_shift.y() / scale_factor) as CGFloat,
-                    )],
-                    cx,
-                );
-
-            if let RasterizationOptions::Bgra = options {
-                // Convert from RGBA with premultiplied alpha to BGRA with straight alpha.
-                for pixel in bytes.chunks_exact_mut(4) {
-                    pixel.swap(0, 2);
-                    let a = pixel[3] as f32 / 255.;
-                    pixel[0] = (pixel[0] as f32 / a) as u8;
-                    pixel[1] = (pixel[1] as f32 / a) as u8;
-                    pixel[2] = (pixel[2] as f32 / a) as u8;
-                }
-            }
-
-            Some((cx_bounds, bytes))
-        }
-    }
-
-    fn layout_line(
-        &mut self,
-        text: &str,
-        font_size: f32,
-        runs: &[(usize, RunStyle)],
-    ) -> LineLayout {
-        // Construct the attributed string, converting UTF8 ranges to UTF16 ranges.
-        let mut string = CFMutableAttributedString::new();
-        {
-            string.replace_str(&CFString::new(text), CFRange::init(0, 0));
-            let utf16_line_len = string.char_len() as usize;
-
-            let last_run: RefCell<Option<(usize, FontId)>> = Default::default();
-            let font_runs = runs
-                .iter()
-                .filter_map(|(len, style)| {
-                    let mut last_run = last_run.borrow_mut();
-                    if let Some((last_len, last_font_id)) = last_run.as_mut() {
-                        if style.font_id == *last_font_id {
-                            *last_len += *len;
-                            None
-                        } else {
-                            let result = (*last_len, *last_font_id);
-                            *last_len = *len;
-                            *last_font_id = style.font_id;
-                            Some(result)
-                        }
-                    } else {
-                        *last_run = Some((*len, style.font_id));
-                        None
-                    }
-                })
-                .chain(std::iter::from_fn(|| last_run.borrow_mut().take()));
-
-            let mut ix_converter = StringIndexConverter::new(text);
-            for (run_len, font_id) in font_runs {
-                let utf8_end = ix_converter.utf8_ix + run_len;
-                let utf16_start = ix_converter.utf16_ix;
-
-                if utf16_start >= utf16_line_len {
-                    break;
-                }
-
-                ix_converter.advance_to_utf8_ix(utf8_end);
-                let utf16_end = cmp::min(ix_converter.utf16_ix, utf16_line_len);
-
-                let cf_range =
-                    CFRange::init(utf16_start as isize, (utf16_end - utf16_start) as isize);
-                let font = &self.fonts[font_id.0];
-                unsafe {
-                    string.set_attribute(
-                        cf_range,
-                        kCTFontAttributeName,
-                        &font.native_font().clone_with_font_size(font_size as f64),
-                    );
-                }
-
-                if utf16_end == utf16_line_len {
-                    break;
-                }
-            }
-        }
-
-        // Retrieve the glyphs from the shaped line, converting UTF16 offsets to UTF8 offsets.
-        let line = CTLine::new_with_attributed_string(string.as_concrete_TypeRef());
-
-        let mut runs = Vec::new();
-        for run in line.glyph_runs().into_iter() {
-            let attributes = run.attributes().unwrap();
-            let font = unsafe {
-                attributes
-                    .get(kCTFontAttributeName)
-                    .downcast::<CTFont>()
-                    .unwrap()
-            };
-            let font_id = self.id_for_native_font(font);
-
-            let mut ix_converter = StringIndexConverter::new(text);
-            let mut glyphs = Vec::new();
-            for ((glyph_id, position), glyph_utf16_ix) in run
-                .glyphs()
-                .iter()
-                .zip(run.positions().iter())
-                .zip(run.string_indices().iter())
-            {
-                let glyph_utf16_ix = usize::try_from(*glyph_utf16_ix).unwrap();
-                ix_converter.advance_to_utf16_ix(glyph_utf16_ix);
-                glyphs.push(Glyph {
-                    id: *glyph_id as GlyphId,
-                    position: vec2f(position.x as f32, position.y as f32),
-                    index: ix_converter.utf8_ix,
-                    is_emoji: self.is_emoji(font_id),
-                });
-            }
-
-            runs.push(Run { font_id, glyphs })
-        }
-
-        let typographic_bounds = line.get_typographic_bounds();
-        LineLayout {
-            width: typographic_bounds.width as f32,
-            ascent: typographic_bounds.ascent as f32,
-            descent: typographic_bounds.descent as f32,
-            runs,
-            font_size,
-            len: text.len(),
-        }
-    }
-
-    fn wrap_line(&self, text: &str, font_id: FontId, font_size: f32, width: f32) -> Vec<usize> {
-        let mut string = CFMutableAttributedString::new();
-        string.replace_str(&CFString::new(text), CFRange::init(0, 0));
-        let cf_range = CFRange::init(0, text.encode_utf16().count() as isize);
-        let font = &self.fonts[font_id.0];
-        unsafe {
-            string.set_attribute(
-                cf_range,
-                kCTFontAttributeName,
-                &font.native_font().clone_with_font_size(font_size as f64),
-            );
-
-            let typesetter = CTTypesetterCreateWithAttributedString(string.as_concrete_TypeRef());
-            let mut ix_converter = StringIndexConverter::new(text);
-            let mut break_indices = Vec::new();
-            while ix_converter.utf8_ix < text.len() {
-                let utf16_len = CTTypesetterSuggestLineBreak(
-                    typesetter,
-                    ix_converter.utf16_ix as isize,
-                    width as f64,
-                ) as usize;
-                ix_converter.advance_to_utf16_ix(ix_converter.utf16_ix + utf16_len);
-                if ix_converter.utf8_ix >= text.len() {
-                    break;
-                }
-                break_indices.push(ix_converter.utf8_ix as usize);
-            }
-            break_indices
-        }
-    }
-}
-
-#[derive(Clone)]
-struct StringIndexConverter<'a> {
-    text: &'a str,
-    utf8_ix: usize,
-    utf16_ix: usize,
-}
-
-impl<'a> StringIndexConverter<'a> {
-    fn new(text: &'a str) -> Self {
-        Self {
-            text,
-            utf8_ix: 0,
-            utf16_ix: 0,
-        }
-    }
-
-    fn advance_to_utf8_ix(&mut self, utf8_target: usize) {
-        for (ix, c) in self.text[self.utf8_ix..].char_indices() {
-            if self.utf8_ix + ix >= utf8_target {
-                self.utf8_ix += ix;
-                return;
-            }
-            self.utf16_ix += c.len_utf16();
-        }
-        self.utf8_ix = self.text.len();
-    }
-
-    fn advance_to_utf16_ix(&mut self, utf16_target: usize) {
-        for (ix, c) in self.text[self.utf8_ix..].char_indices() {
-            if self.utf16_ix >= utf16_target {
-                self.utf8_ix += ix;
-                return;
-            }
-            self.utf16_ix += c.len_utf16();
-        }
-        self.utf8_ix = self.text.len();
-    }
-}
-
-#[repr(C)]
-pub struct __CFTypesetter(c_void);
-
-pub type CTTypesetterRef = *const __CFTypesetter;
-
-#[link(name = "CoreText", kind = "framework")]
-extern "C" {
-    fn CTTypesetterCreateWithAttributedString(string: CFAttributedStringRef) -> CTTypesetterRef;
-
-    fn CTTypesetterSuggestLineBreak(
-        typesetter: CTTypesetterRef,
-        start_index: CFIndex,
-        width: f64,
-    ) -> CFIndex;
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::AppContext;
-    use font_kit::properties::{Style, Weight};
-    use platform::FontSystem as _;
-
-    #[crate::test(self, retries = 5)]
-    fn test_layout_str(_: &mut AppContext) {
-        // This is failing intermittently on CI and we don't have time to figure it out
-        let fonts = FontSystem::new();
-        let menlo = fonts.load_family("Menlo", &Default::default()).unwrap();
-        let menlo_regular = RunStyle {
-            font_id: fonts.select_font(&menlo, &Properties::new()).unwrap(),
-            color: Default::default(),
-            underline: Default::default(),
-        };
-        let menlo_italic = RunStyle {
-            font_id: fonts
-                .select_font(&menlo, Properties::new().style(Style::Italic))
-                .unwrap(),
-            color: Default::default(),
-            underline: Default::default(),
-        };
-        let menlo_bold = RunStyle {
-            font_id: fonts
-                .select_font(&menlo, Properties::new().weight(Weight::BOLD))
-                .unwrap(),
-            color: Default::default(),
-            underline: Default::default(),
-        };
-        assert_ne!(menlo_regular, menlo_italic);
-        assert_ne!(menlo_regular, menlo_bold);
-        assert_ne!(menlo_italic, menlo_bold);
-
-        let line = fonts.layout_line(
-            "hello world",
-            16.0,
-            &[(2, menlo_bold), (4, menlo_italic), (5, menlo_regular)],
-        );
-        assert_eq!(line.runs.len(), 3);
-        assert_eq!(line.runs[0].font_id, menlo_bold.font_id);
-        assert_eq!(line.runs[0].glyphs.len(), 2);
-        assert_eq!(line.runs[1].font_id, menlo_italic.font_id);
-        assert_eq!(line.runs[1].glyphs.len(), 4);
-        assert_eq!(line.runs[2].font_id, menlo_regular.font_id);
-        assert_eq!(line.runs[2].glyphs.len(), 5);
-    }
-
-    #[test]
-    fn test_glyph_offsets() -> anyhow::Result<()> {
-        let fonts = FontSystem::new();
-        let zapfino = fonts.load_family("Zapfino", &Default::default())?;
-        let zapfino_regular = RunStyle {
-            font_id: fonts.select_font(&zapfino, &Properties::new())?,
-            color: Default::default(),
-            underline: Default::default(),
-        };
-        let menlo = fonts.load_family("Menlo", &Default::default())?;
-        let menlo_regular = RunStyle {
-            font_id: fonts.select_font(&menlo, &Properties::new())?,
-            color: Default::default(),
-            underline: Default::default(),
-        };
-
-        let text = "This is, m𐍈re 𐍈r less, Zapfino!𐍈";
-        let line = fonts.layout_line(
-            text,
-            16.0,
-            &[
-                (9, zapfino_regular),
-                (13, menlo_regular),
-                (text.len() - 22, zapfino_regular),
-            ],
-        );
-        assert_eq!(
-            line.runs
-                .iter()
-                .flat_map(|r| r.glyphs.iter())
-                .map(|g| g.index)
-                .collect::<Vec<_>>(),
-            vec![0, 2, 4, 5, 7, 8, 9, 10, 14, 15, 16, 17, 21, 22, 23, 24, 26, 27, 28, 29, 36, 37],
-        );
-        Ok(())
-    }
-
-    #[test]
-    #[ignore]
-    fn test_rasterize_glyph() {
-        use std::{fs::File, io::BufWriter, path::Path};
-
-        let fonts = FontSystem::new();
-        let font_ids = fonts.load_family("Fira Code", &Default::default()).unwrap();
-        let font_id = fonts.select_font(&font_ids, &Default::default()).unwrap();
-        let glyph_id = fonts.glyph_for_char(font_id, 'G').unwrap();
-
-        const VARIANTS: usize = 1;
-        for i in 0..VARIANTS {
-            let variant = i as f32 / VARIANTS as f32;
-            let (bounds, bytes) = fonts
-                .rasterize_glyph(
-                    font_id,
-                    16.0,
-                    glyph_id,
-                    vec2f(variant, variant),
-                    2.,
-                    RasterizationOptions::Alpha,
-                )
-                .unwrap();
-
-            let name = format!("/Users/as-cii/Desktop/twog-{}.png", i);
-            let path = Path::new(&name);
-            let file = File::create(path).unwrap();
-            let w = &mut BufWriter::new(file);
-
-            let mut encoder = png::Encoder::new(w, bounds.width() as u32, bounds.height() as u32);
-            encoder.set_color(png::ColorType::Grayscale);
-            encoder.set_depth(png::BitDepth::Eight);
-            let mut writer = encoder.write_header().unwrap();
-            writer.write_image_data(&bytes).unwrap();
-        }
-    }
-
-    #[test]
-    fn test_wrap_line() {
-        let fonts = FontSystem::new();
-        let font_ids = fonts.load_family("Helvetica", &Default::default()).unwrap();
-        let font_id = fonts.select_font(&font_ids, &Default::default()).unwrap();
-
-        let line = "one two three four five\n";
-        let wrap_boundaries = fonts.wrap_line(line, font_id, 16., 64.0);
-        assert_eq!(wrap_boundaries, &["one two ".len(), "one two three ".len()]);
-
-        let line = "aaa ααα ✋✋✋ 🎉🎉🎉\n";
-        let wrap_boundaries = fonts.wrap_line(line, font_id, 16., 64.0);
-        assert_eq!(
-            wrap_boundaries,
-            &["aaa ααα ".len(), "aaa ααα ✋✋✋ ".len(),]
-        );
-    }
-
-    #[test]
-    fn test_layout_line_bom_char() {
-        let fonts = FontSystem::new();
-        let font_ids = fonts.load_family("Helvetica", &Default::default()).unwrap();
-        let style = RunStyle {
-            font_id: fonts.select_font(&font_ids, &Default::default()).unwrap(),
-            color: Default::default(),
-            underline: Default::default(),
-        };
-
-        let line = "\u{feff}";
-        let layout = fonts.layout_line(line, 16., &[(line.len(), style)]);
-        assert_eq!(layout.len, line.len());
-        assert!(layout.runs.is_empty());
-
-        let line = "a\u{feff}b";
-        let layout = fonts.layout_line(line, 16., &[(line.len(), style)]);
-        assert_eq!(layout.len, line.len());
-        assert_eq!(layout.runs.len(), 1);
-        assert_eq!(layout.runs[0].glyphs.len(), 2);
-        assert_eq!(layout.runs[0].glyphs[0].id, 68); // a
-                                                     // There's no glyph for \u{feff}
-        assert_eq!(layout.runs[0].glyphs[1].id, 69); // b
-    }
-}

crates/gpui/src/platform/mac/fonts/open_type.rs 🔗

@@ -1,395 +0,0 @@
-#![allow(unused, non_upper_case_globals)]
-
-use std::ptr;
-
-use crate::fonts::Features;
-use cocoa::appkit::CGFloat;
-use core_foundation::{base::TCFType, number::CFNumber};
-use core_graphics::geometry::CGAffineTransform;
-use core_text::{
-    font::{CTFont, CTFontRef},
-    font_descriptor::{
-        CTFontDescriptor, CTFontDescriptorCreateCopyWithFeature, CTFontDescriptorRef,
-    },
-};
-use font_kit::font::Font;
-
-const kCaseSensitiveLayoutOffSelector: i32 = 1;
-const kCaseSensitiveLayoutOnSelector: i32 = 0;
-const kCaseSensitiveLayoutType: i32 = 33;
-const kCaseSensitiveSpacingOffSelector: i32 = 3;
-const kCaseSensitiveSpacingOnSelector: i32 = 2;
-const kCharacterAlternativesType: i32 = 17;
-const kCommonLigaturesOffSelector: i32 = 3;
-const kCommonLigaturesOnSelector: i32 = 2;
-const kContextualAlternatesOffSelector: i32 = 1;
-const kContextualAlternatesOnSelector: i32 = 0;
-const kContextualAlternatesType: i32 = 36;
-const kContextualLigaturesOffSelector: i32 = 19;
-const kContextualLigaturesOnSelector: i32 = 18;
-const kContextualSwashAlternatesOffSelector: i32 = 5;
-const kContextualSwashAlternatesOnSelector: i32 = 4;
-const kDefaultLowerCaseSelector: i32 = 0;
-const kDefaultUpperCaseSelector: i32 = 0;
-const kDiagonalFractionsSelector: i32 = 2;
-const kFractionsType: i32 = 11;
-const kHistoricalLigaturesOffSelector: i32 = 21;
-const kHistoricalLigaturesOnSelector: i32 = 20;
-const kHojoCharactersSelector: i32 = 12;
-const kInferiorsSelector: i32 = 2;
-const kJIS2004CharactersSelector: i32 = 11;
-const kLigaturesType: i32 = 1;
-const kLowerCasePetiteCapsSelector: i32 = 2;
-const kLowerCaseSmallCapsSelector: i32 = 1;
-const kLowerCaseType: i32 = 37;
-const kLowerCaseNumbersSelector: i32 = 0;
-const kMathematicalGreekOffSelector: i32 = 11;
-const kMathematicalGreekOnSelector: i32 = 10;
-const kMonospacedNumbersSelector: i32 = 0;
-const kNLCCharactersSelector: i32 = 13;
-const kNoFractionsSelector: i32 = 0;
-const kNormalPositionSelector: i32 = 0;
-const kNoStyleOptionsSelector: i32 = 0;
-const kNumberCaseType: i32 = 21;
-const kNumberSpacingType: i32 = 6;
-const kOrdinalsSelector: i32 = 3;
-const kProportionalNumbersSelector: i32 = 1;
-const kQuarterWidthTextSelector: i32 = 4;
-const kScientificInferiorsSelector: i32 = 4;
-const kSlashedZeroOffSelector: i32 = 5;
-const kSlashedZeroOnSelector: i32 = 4;
-const kStyleOptionsType: i32 = 19;
-const kStylisticAltEighteenOffSelector: i32 = 37;
-const kStylisticAltEighteenOnSelector: i32 = 36;
-const kStylisticAltEightOffSelector: i32 = 17;
-const kStylisticAltEightOnSelector: i32 = 16;
-const kStylisticAltElevenOffSelector: i32 = 23;
-const kStylisticAltElevenOnSelector: i32 = 22;
-const kStylisticAlternativesType: i32 = 35;
-const kStylisticAltFifteenOffSelector: i32 = 31;
-const kStylisticAltFifteenOnSelector: i32 = 30;
-const kStylisticAltFiveOffSelector: i32 = 11;
-const kStylisticAltFiveOnSelector: i32 = 10;
-const kStylisticAltFourOffSelector: i32 = 9;
-const kStylisticAltFourOnSelector: i32 = 8;
-const kStylisticAltFourteenOffSelector: i32 = 29;
-const kStylisticAltFourteenOnSelector: i32 = 28;
-const kStylisticAltNineOffSelector: i32 = 19;
-const kStylisticAltNineOnSelector: i32 = 18;
-const kStylisticAltNineteenOffSelector: i32 = 39;
-const kStylisticAltNineteenOnSelector: i32 = 38;
-const kStylisticAltOneOffSelector: i32 = 3;
-const kStylisticAltOneOnSelector: i32 = 2;
-const kStylisticAltSevenOffSelector: i32 = 15;
-const kStylisticAltSevenOnSelector: i32 = 14;
-const kStylisticAltSeventeenOffSelector: i32 = 35;
-const kStylisticAltSeventeenOnSelector: i32 = 34;
-const kStylisticAltSixOffSelector: i32 = 13;
-const kStylisticAltSixOnSelector: i32 = 12;
-const kStylisticAltSixteenOffSelector: i32 = 33;
-const kStylisticAltSixteenOnSelector: i32 = 32;
-const kStylisticAltTenOffSelector: i32 = 21;
-const kStylisticAltTenOnSelector: i32 = 20;
-const kStylisticAltThirteenOffSelector: i32 = 27;
-const kStylisticAltThirteenOnSelector: i32 = 26;
-const kStylisticAltThreeOffSelector: i32 = 7;
-const kStylisticAltThreeOnSelector: i32 = 6;
-const kStylisticAltTwelveOffSelector: i32 = 25;
-const kStylisticAltTwelveOnSelector: i32 = 24;
-const kStylisticAltTwentyOffSelector: i32 = 41;
-const kStylisticAltTwentyOnSelector: i32 = 40;
-const kStylisticAltTwoOffSelector: i32 = 5;
-const kStylisticAltTwoOnSelector: i32 = 4;
-const kSuperiorsSelector: i32 = 1;
-const kSwashAlternatesOffSelector: i32 = 3;
-const kSwashAlternatesOnSelector: i32 = 2;
-const kTitlingCapsSelector: i32 = 4;
-const kTypographicExtrasType: i32 = 14;
-const kVerticalFractionsSelector: i32 = 1;
-const kVerticalPositionType: i32 = 10;
-
-pub fn apply_features(font: &mut Font, features: &Features) {
-    // See https://chromium.googlesource.com/chromium/src/+/66.0.3359.158/third_party/harfbuzz-ng/src/hb-coretext.cc
-    // for a reference implementation.
-    toggle_open_type_feature(
-        font,
-        features.calt,
-        kContextualAlternatesType,
-        kContextualAlternatesOnSelector,
-        kContextualAlternatesOffSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.case,
-        kCaseSensitiveLayoutType,
-        kCaseSensitiveLayoutOnSelector,
-        kCaseSensitiveLayoutOffSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.cpsp,
-        kCaseSensitiveLayoutType,
-        kCaseSensitiveSpacingOnSelector,
-        kCaseSensitiveSpacingOffSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.frac,
-        kFractionsType,
-        kDiagonalFractionsSelector,
-        kNoFractionsSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.liga,
-        kLigaturesType,
-        kCommonLigaturesOnSelector,
-        kCommonLigaturesOffSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.onum,
-        kNumberCaseType,
-        kLowerCaseNumbersSelector,
-        2,
-    );
-    toggle_open_type_feature(
-        font,
-        features.ordn,
-        kVerticalPositionType,
-        kOrdinalsSelector,
-        kNormalPositionSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.pnum,
-        kNumberSpacingType,
-        kProportionalNumbersSelector,
-        4,
-    );
-    toggle_open_type_feature(
-        font,
-        features.ss01,
-        kStylisticAlternativesType,
-        kStylisticAltOneOnSelector,
-        kStylisticAltOneOffSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.ss02,
-        kStylisticAlternativesType,
-        kStylisticAltTwoOnSelector,
-        kStylisticAltTwoOffSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.ss03,
-        kStylisticAlternativesType,
-        kStylisticAltThreeOnSelector,
-        kStylisticAltThreeOffSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.ss04,
-        kStylisticAlternativesType,
-        kStylisticAltFourOnSelector,
-        kStylisticAltFourOffSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.ss05,
-        kStylisticAlternativesType,
-        kStylisticAltFiveOnSelector,
-        kStylisticAltFiveOffSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.ss06,
-        kStylisticAlternativesType,
-        kStylisticAltSixOnSelector,
-        kStylisticAltSixOffSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.ss07,
-        kStylisticAlternativesType,
-        kStylisticAltSevenOnSelector,
-        kStylisticAltSevenOffSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.ss08,
-        kStylisticAlternativesType,
-        kStylisticAltEightOnSelector,
-        kStylisticAltEightOffSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.ss09,
-        kStylisticAlternativesType,
-        kStylisticAltNineOnSelector,
-        kStylisticAltNineOffSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.ss10,
-        kStylisticAlternativesType,
-        kStylisticAltTenOnSelector,
-        kStylisticAltTenOffSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.ss11,
-        kStylisticAlternativesType,
-        kStylisticAltElevenOnSelector,
-        kStylisticAltElevenOffSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.ss12,
-        kStylisticAlternativesType,
-        kStylisticAltTwelveOnSelector,
-        kStylisticAltTwelveOffSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.ss13,
-        kStylisticAlternativesType,
-        kStylisticAltThirteenOnSelector,
-        kStylisticAltThirteenOffSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.ss14,
-        kStylisticAlternativesType,
-        kStylisticAltFourteenOnSelector,
-        kStylisticAltFourteenOffSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.ss15,
-        kStylisticAlternativesType,
-        kStylisticAltFifteenOnSelector,
-        kStylisticAltFifteenOffSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.ss16,
-        kStylisticAlternativesType,
-        kStylisticAltSixteenOnSelector,
-        kStylisticAltSixteenOffSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.ss17,
-        kStylisticAlternativesType,
-        kStylisticAltSeventeenOnSelector,
-        kStylisticAltSeventeenOffSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.ss18,
-        kStylisticAlternativesType,
-        kStylisticAltEighteenOnSelector,
-        kStylisticAltEighteenOffSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.ss19,
-        kStylisticAlternativesType,
-        kStylisticAltNineteenOnSelector,
-        kStylisticAltNineteenOffSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.ss20,
-        kStylisticAlternativesType,
-        kStylisticAltTwentyOnSelector,
-        kStylisticAltTwentyOffSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.subs,
-        kVerticalPositionType,
-        kInferiorsSelector,
-        kNormalPositionSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.sups,
-        kVerticalPositionType,
-        kSuperiorsSelector,
-        kNormalPositionSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.swsh,
-        kContextualAlternatesType,
-        kSwashAlternatesOnSelector,
-        kSwashAlternatesOffSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.titl,
-        kStyleOptionsType,
-        kTitlingCapsSelector,
-        kNoStyleOptionsSelector,
-    );
-    toggle_open_type_feature(
-        font,
-        features.tnum,
-        kNumberSpacingType,
-        kMonospacedNumbersSelector,
-        4,
-    );
-    toggle_open_type_feature(
-        font,
-        features.zero,
-        kTypographicExtrasType,
-        kSlashedZeroOnSelector,
-        kSlashedZeroOffSelector,
-    );
-}
-
-fn toggle_open_type_feature(
-    font: &mut Font,
-    enabled: Option<bool>,
-    type_identifier: i32,
-    on_selector_identifier: i32,
-    off_selector_identifier: i32,
-) {
-    if let Some(enabled) = enabled {
-        let native_font = font.native_font();
-        unsafe {
-            let selector_identifier = if enabled {
-                on_selector_identifier
-            } else {
-                off_selector_identifier
-            };
-            let new_descriptor = CTFontDescriptorCreateCopyWithFeature(
-                native_font.copy_descriptor().as_concrete_TypeRef(),
-                CFNumber::from(type_identifier).as_concrete_TypeRef(),
-                CFNumber::from(selector_identifier).as_concrete_TypeRef(),
-            );
-            let new_descriptor = CTFontDescriptor::wrap_under_create_rule(new_descriptor);
-            let new_font = CTFontCreateCopyWithAttributes(
-                font.native_font().as_concrete_TypeRef(),
-                0.0,
-                ptr::null(),
-                new_descriptor.as_concrete_TypeRef(),
-            );
-            let new_font = CTFont::wrap_under_create_rule(new_font);
-            *font = Font::from_native_font(new_font);
-        }
-    }
-}
-
-#[link(name = "CoreText", kind = "framework")]
-extern "C" {
-    fn CTFontCreateCopyWithAttributes(
-        font: CTFontRef,
-        size: CGFloat,
-        matrix: *const CGAffineTransform,
-        attributes: CTFontDescriptorRef,
-    ) -> CTFontRef;
-}

crates/gpui/src/platform/mac/geometry.rs 🔗

@@ -1,45 +0,0 @@
-use cocoa::{
-    base::id,
-    foundation::{NSPoint, NSRect},
-};
-use objc::{msg_send, sel, sel_impl};
-use pathfinder_geometry::vector::{vec2f, Vector2F};
-
-///! Macos screen have a y axis that goings up from the bottom of the screen and
-///! an origin at the bottom left of the main display.
-
-pub trait Vector2FExt {
-    /// Converts self to an NSPoint with y axis pointing up.
-    fn to_screen_ns_point(&self, native_window: id, window_height: f64) -> NSPoint;
-}
-
-impl Vector2FExt for Vector2F {
-    fn to_screen_ns_point(&self, native_window: id, window_height: f64) -> NSPoint {
-        unsafe {
-            let point = NSPoint::new(self.x() as f64, window_height - self.y() as f64);
-            msg_send![native_window, convertPointToScreen: point]
-        }
-    }
-}
-
-pub trait NSRectExt {
-    fn size_vec(&self) -> Vector2F;
-    fn intersects(&self, other: Self) -> bool;
-}
-
-impl NSRectExt for NSRect {
-    fn size_vec(&self) -> Vector2F {
-        vec2f(self.size.width as f32, self.size.height as f32)
-    }
-
-    fn intersects(&self, other: Self) -> bool {
-        self.size.width > 0.
-            && self.size.height > 0.
-            && other.size.width > 0.
-            && other.size.height > 0.
-            && self.origin.x <= other.origin.x + other.size.width
-            && self.origin.x + self.size.width >= other.origin.x
-            && self.origin.y <= other.origin.y + other.size.height
-            && self.origin.y + self.size.height >= other.origin.y
-    }
-}

crates/gpui/src/platform/mac/image_cache.rs 🔗

@@ -1,115 +0,0 @@
-use super::atlas::{AllocId, AtlasAllocator};
-use crate::{
-    fonts::{FontId, GlyphId},
-    geometry::{rect::RectI, vector::Vector2I},
-    platform::{FontSystem, RasterizationOptions},
-    scene::ImageGlyph,
-    ImageData,
-};
-use anyhow::anyhow;
-use metal::{MTLPixelFormat, TextureDescriptor, TextureRef};
-use ordered_float::OrderedFloat;
-use std::{collections::HashMap, mem, sync::Arc};
-
-#[derive(Hash, Eq, PartialEq)]
-struct GlyphDescriptor {
-    font_id: FontId,
-    font_size: OrderedFloat<f32>,
-    glyph_id: GlyphId,
-}
-
-pub struct ImageCache {
-    prev_frame: HashMap<usize, (AllocId, RectI)>,
-    curr_frame: HashMap<usize, (AllocId, RectI)>,
-    image_glyphs: HashMap<GlyphDescriptor, Option<(AllocId, RectI, Vector2I)>>,
-    atlases: AtlasAllocator,
-    scale_factor: f32,
-    fonts: Arc<dyn FontSystem>,
-}
-
-impl ImageCache {
-    pub fn new(
-        device: metal::Device,
-        size: Vector2I,
-        scale_factor: f32,
-        fonts: Arc<dyn FontSystem>,
-    ) -> Self {
-        let descriptor = TextureDescriptor::new();
-        descriptor.set_pixel_format(MTLPixelFormat::BGRA8Unorm);
-        descriptor.set_width(size.x() as u64);
-        descriptor.set_height(size.y() as u64);
-        Self {
-            prev_frame: Default::default(),
-            curr_frame: Default::default(),
-            image_glyphs: Default::default(),
-            atlases: AtlasAllocator::new(device, descriptor),
-            scale_factor,
-            fonts,
-        }
-    }
-
-    pub fn set_scale_factor(&mut self, scale_factor: f32) {
-        if scale_factor != self.scale_factor {
-            self.scale_factor = scale_factor;
-            for (_, glyph) in self.image_glyphs.drain() {
-                if let Some((alloc_id, _, _)) = glyph {
-                    self.atlases.deallocate(alloc_id);
-                }
-            }
-        }
-    }
-
-    pub fn render(&mut self, image: &ImageData) -> (AllocId, RectI) {
-        let (alloc_id, atlas_bounds) = self
-            .prev_frame
-            .remove(&image.id)
-            .or_else(|| self.curr_frame.get(&image.id).copied())
-            .or_else(|| self.atlases.upload(image.size(), image.as_bytes()))
-            .ok_or_else(|| anyhow!("could not upload image of size {:?}", image.size()))
-            .unwrap();
-        self.curr_frame.insert(image.id, (alloc_id, atlas_bounds));
-        (alloc_id, atlas_bounds)
-    }
-
-    pub fn render_glyph(&mut self, image_glyph: &ImageGlyph) -> Option<(AllocId, RectI, Vector2I)> {
-        *self
-            .image_glyphs
-            .entry(GlyphDescriptor {
-                font_id: image_glyph.font_id,
-                font_size: OrderedFloat(image_glyph.font_size),
-                glyph_id: image_glyph.id,
-            })
-            .or_insert_with(|| {
-                let (glyph_bounds, bytes) = self.fonts.rasterize_glyph(
-                    image_glyph.font_id,
-                    image_glyph.font_size,
-                    image_glyph.id,
-                    Default::default(),
-                    self.scale_factor,
-                    RasterizationOptions::Bgra,
-                )?;
-                let (alloc_id, atlas_bounds) = self
-                    .atlases
-                    .upload(glyph_bounds.size(), &bytes)
-                    .ok_or_else(|| {
-                        anyhow!(
-                            "could not upload image glyph of size {:?}",
-                            glyph_bounds.size()
-                        )
-                    })
-                    .unwrap();
-                Some((alloc_id, atlas_bounds, glyph_bounds.origin()))
-            })
-    }
-
-    pub fn finish_frame(&mut self) {
-        mem::swap(&mut self.prev_frame, &mut self.curr_frame);
-        for (_, (id, _)) in self.curr_frame.drain() {
-            self.atlases.deallocate(id);
-        }
-    }
-
-    pub fn atlas_texture(&self, atlas_id: usize) -> Option<&TextureRef> {
-        self.atlases.texture(atlas_id)
-    }
-}

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

@@ -1,14 +1,12 @@
-use super::{
-    event::key_to_native, screen::Screen, status_item::StatusItem, BoolExt as _, Dispatcher,
-    FontSystem, MacWindow,
-};
+use super::{events::key_to_native, BoolExt};
 use crate::{
-    executor,
-    keymap_matcher::KeymapMatcher,
-    platform::{self, AppVersion, CursorStyle, Event},
-    Action, AnyWindowHandle, ClipboardItem, Menu, MenuItem,
+    Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId,
+    ForegroundExecutor, InputEvent, Keymap, MacDispatcher, MacDisplay, MacDisplayLinker,
+    MacTextSystem, MacWindow, Menu, MenuItem, PathPromptOptions, Platform, PlatformDisplay,
+    PlatformTextSystem, PlatformWindow, Result, Scene, SemanticVersion, VideoTimestamp,
+    WindowOptions,
 };
-use anyhow::{anyhow, Result};
+use anyhow::anyhow;
 use block::ConcreteBlock;
 use cocoa::{
     appkit::{
@@ -30,6 +28,7 @@ use core_foundation::{
     string::{CFString, CFStringRef},
 };
 use ctor::ctor;
+use futures::channel::oneshot;
 use objc::{
     class,
     declare::ClassDecl,
@@ -37,11 +36,10 @@ use objc::{
     runtime::{Class, Object, Sel},
     sel, sel_impl,
 };
-
-use postage::oneshot;
+use parking_lot::Mutex;
 use ptr::null_mut;
 use std::{
-    cell::{Cell, RefCell},
+    cell::Cell,
     convert::TryInto,
     ffi::{c_void, CStr, OsStr},
     os::{raw::c_char, unix::ffi::OsStrExt},
@@ -51,6 +49,7 @@ use std::{
     rc::Rc,
     slice, str,
     sync::Arc,
+    time::Duration,
 };
 use time::UtcOffset;
 
@@ -140,34 +139,50 @@ unsafe fn build_classes() {
             sel!(application:openURLs:),
             open_urls as extern "C" fn(&mut Object, Sel, id, id),
         );
-        decl.add_method(
-            sel!(application:continueUserActivity:restorationHandler:),
-            continue_user_activity as extern "C" fn(&mut Object, Sel, id, id, id),
-        );
         decl.register()
     }
 }
 
-pub struct MacForegroundPlatform(RefCell<MacForegroundPlatformState>);
+pub struct MacPlatform(Mutex<MacPlatformState>);
 
-pub struct MacForegroundPlatformState {
+pub struct MacPlatformState {
+    background_executor: BackgroundExecutor,
+    foreground_executor: ForegroundExecutor,
+    text_system: Arc<MacTextSystem>,
+    display_linker: MacDisplayLinker,
+    pasteboard: id,
+    text_hash_pasteboard_type: id,
+    metadata_pasteboard_type: id,
     become_active: Option<Box<dyn FnMut()>>,
     resign_active: Option<Box<dyn FnMut()>>,
     reopen: Option<Box<dyn FnMut()>>,
     quit: Option<Box<dyn FnMut()>>,
-    event: Option<Box<dyn FnMut(platform::Event) -> bool>>,
+    event: Option<Box<dyn FnMut(InputEvent) -> bool>>,
     menu_command: Option<Box<dyn FnMut(&dyn Action)>>,
     validate_menu_command: Option<Box<dyn FnMut(&dyn Action) -> bool>>,
     will_open_menu: Option<Box<dyn FnMut()>>,
+    menu_actions: Vec<Box<dyn Action>>,
     open_urls: Option<Box<dyn FnMut(Vec<String>)>>,
     finish_launching: Option<Box<dyn FnOnce()>>,
-    menu_actions: Vec<Box<dyn Action>>,
-    foreground: Rc<executor::Foreground>,
 }
 
-impl MacForegroundPlatform {
-    pub fn new(foreground: Rc<executor::Foreground>) -> Self {
-        Self(RefCell::new(MacForegroundPlatformState {
+impl Default for MacPlatform {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+impl MacPlatform {
+    pub fn new() -> Self {
+        let dispatcher = Arc::new(MacDispatcher::new());
+        Self(Mutex::new(MacPlatformState {
+            background_executor: BackgroundExecutor::new(dispatcher.clone()),
+            foreground_executor: ForegroundExecutor::new(dispatcher),
+            text_system: Arc::new(MacTextSystem::new()),
+            display_linker: MacDisplayLinker::new(),
+            pasteboard: unsafe { NSPasteboard::generalPasteboard(nil) },
+            text_hash_pasteboard_type: unsafe { ns_string("zed-text-hash") },
+            metadata_pasteboard_type: unsafe { ns_string("zed-metadata") },
             become_active: None,
             resign_active: None,
             reopen: None,
@@ -176,19 +191,30 @@ impl MacForegroundPlatform {
             menu_command: None,
             validate_menu_command: None,
             will_open_menu: None,
+            menu_actions: Default::default(),
             open_urls: None,
             finish_launching: None,
-            menu_actions: Default::default(),
-            foreground,
         }))
     }
 
+    unsafe fn read_from_pasteboard(&self, pasteboard: *mut Object, kind: id) -> Option<&[u8]> {
+        let data = pasteboard.dataForType(kind);
+        if data == nil {
+            None
+        } else {
+            Some(slice::from_raw_parts(
+                data.bytes() as *mut u8,
+                data.length() as usize,
+            ))
+        }
+    }
+
     unsafe fn create_menu_bar(
         &self,
         menus: Vec<Menu>,
         delegate: id,
         actions: &mut Vec<Box<dyn Action>>,
-        keystroke_matcher: &KeymapMatcher,
+        keymap: &Keymap,
     ) -> id {
         let application_menu = NSMenu::new(nil).autorelease();
         application_menu.setDelegate_(delegate);
@@ -199,11 +225,11 @@ impl MacForegroundPlatform {
             menu.setDelegate_(delegate);
 
             for item_config in menu_config.items {
-                menu.addItem_(self.create_menu_item(
+                menu.addItem_(Self::create_menu_item(
                     item_config,
                     delegate,
                     actions,
-                    keystroke_matcher,
+                    keymap,
                 ));
             }
 
@@ -221,11 +247,10 @@ impl MacForegroundPlatform {
     }
 
     unsafe fn create_menu_item(
-        &self,
         item: MenuItem,
         delegate: id,
         actions: &mut Vec<Box<dyn Action>>,
-        keystroke_matcher: &KeymapMatcher,
+        keymap: &Keymap,
     ) -> id {
         match item {
             MenuItem::Separator => NSMenuItem::separatorItem(nil),
@@ -234,11 +259,11 @@ impl MacForegroundPlatform {
                 action,
                 os_action,
             } => {
-                // TODO
-                let keystrokes = keystroke_matcher
-                    .bindings_for_action(action.id())
-                    .find(|binding| binding.action().eq(action.as_ref()))
+                let keystrokes = keymap
+                    .bindings_for_action(action.type_id())
+                    .find(|binding| binding.action().partial_eq(action.as_ref()))
                     .map(|binding| binding.keystrokes());
+
                 let selector = match os_action {
                     Some(crate::OsAction::Cut) => selector("cut:"),
                     Some(crate::OsAction::Copy) => selector("copy:"),
@@ -255,10 +280,22 @@ impl MacForegroundPlatform {
                         let keystroke = &keystrokes[0];
                         let mut mask = NSEventModifierFlags::empty();
                         for (modifier, flag) in &[
-                            (keystroke.cmd, NSEventModifierFlags::NSCommandKeyMask),
-                            (keystroke.ctrl, NSEventModifierFlags::NSControlKeyMask),
-                            (keystroke.alt, NSEventModifierFlags::NSAlternateKeyMask),
-                            (keystroke.shift, NSEventModifierFlags::NSShiftKeyMask),
+                            (
+                                keystroke.modifiers.command,
+                                NSEventModifierFlags::NSCommandKeyMask,
+                            ),
+                            (
+                                keystroke.modifiers.control,
+                                NSEventModifierFlags::NSControlKeyMask,
+                            ),
+                            (
+                                keystroke.modifiers.alt,
+                                NSEventModifierFlags::NSAlternateKeyMask,
+                            ),
+                            (
+                                keystroke.modifiers.shift,
+                                NSEventModifierFlags::NSShiftKeyMask,
+                            ),
                         ] {
                             if *modifier {
                                 mask |= *flag;
@@ -315,12 +352,7 @@ impl MacForegroundPlatform {
                 let submenu = NSMenu::new(nil).autorelease();
                 submenu.setDelegate_(delegate);
                 for item in items {
-                    submenu.addItem_(self.create_menu_item(
-                        item,
-                        delegate,
-                        actions,
-                        keystroke_matcher,
-                    ));
+                    submenu.addItem_(Self::create_menu_item(item, delegate, actions, keymap));
                 }
                 item.setSubmenu_(submenu);
                 item.setTitle_(ns_string(name));
@@ -330,33 +362,21 @@ impl MacForegroundPlatform {
     }
 }
 
-impl platform::ForegroundPlatform for MacForegroundPlatform {
-    fn on_become_active(&self, callback: Box<dyn FnMut()>) {
-        self.0.borrow_mut().become_active = Some(callback);
-    }
-
-    fn on_resign_active(&self, callback: Box<dyn FnMut()>) {
-        self.0.borrow_mut().resign_active = Some(callback);
+impl Platform for MacPlatform {
+    fn background_executor(&self) -> BackgroundExecutor {
+        self.0.lock().background_executor.clone()
     }
 
-    fn on_quit(&self, callback: Box<dyn FnMut()>) {
-        self.0.borrow_mut().quit = Some(callback);
+    fn foreground_executor(&self) -> crate::ForegroundExecutor {
+        self.0.lock().foreground_executor.clone()
     }
 
-    fn on_reopen(&self, callback: Box<dyn FnMut()>) {
-        self.0.borrow_mut().reopen = Some(callback);
-    }
-
-    fn on_event(&self, callback: Box<dyn FnMut(platform::Event) -> bool>) {
-        self.0.borrow_mut().event = Some(callback);
-    }
-
-    fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>) {
-        self.0.borrow_mut().open_urls = Some(callback);
+    fn text_system(&self) -> Arc<dyn PlatformTextSystem> {
+        self.0.lock().text_system.clone()
     }
 
     fn run(&self, on_finish_launching: Box<dyn FnOnce()>) {
-        self.0.borrow_mut().finish_launching = Some(on_finish_launching);
+        self.0.lock().finish_launching = Some(on_finish_launching);
 
         unsafe {
             let app: id = msg_send![APP_CLASS, sharedApplication];
@@ -376,35 +396,157 @@ impl platform::ForegroundPlatform for MacForegroundPlatform {
         }
     }
 
-    fn on_menu_command(&self, callback: Box<dyn FnMut(&dyn Action)>) {
-        self.0.borrow_mut().menu_command = Some(callback);
+    fn quit(&self) {
+        // Quitting the app causes us to close windows, which invokes `Window::on_close` callbacks
+        // synchronously before this method terminates. If we call `Platform::quit` while holding a
+        // borrow of the app state (which most of the time we will do), we will end up
+        // double-borrowing the app state in the `on_close` callbacks for our open windows. To solve
+        // this, we make quitting the application asynchronous so that we aren't holding borrows to
+        // the app state on the stack when we actually terminate the app.
+
+        use super::dispatcher::{dispatch_async_f, dispatch_get_main_queue};
+
+        unsafe {
+            dispatch_async_f(dispatch_get_main_queue(), ptr::null_mut(), Some(quit));
+        }
+
+        unsafe extern "C" fn quit(_: *mut c_void) {
+            let app = NSApplication::sharedApplication(nil);
+            let _: () = msg_send![app, terminate: nil];
+        }
+    }
+
+    fn restart(&self) {
+        use std::os::unix::process::CommandExt as _;
+
+        let app_pid = std::process::id().to_string();
+        let app_path = self
+            .app_path()
+            .ok()
+            // When the app is not bundled, `app_path` returns the
+            // directory containing the executable. Disregard this
+            // and get the path to the executable itself.
+            .and_then(|path| (path.extension()?.to_str()? == "app").then_some(path))
+            .unwrap_or_else(|| std::env::current_exe().unwrap());
+
+        // Wait until this process has exited and then re-open this path.
+        let script = r#"
+            while kill -0 $0 2> /dev/null; do
+                sleep 0.1
+            done
+            open "$1"
+        "#;
+
+        let restart_process = Command::new("/bin/bash")
+            .arg("-c")
+            .arg(script)
+            .arg(app_pid)
+            .arg(app_path)
+            .process_group(0)
+            .spawn();
+
+        match restart_process {
+            Ok(_) => self.quit(),
+            Err(e) => log::error!("failed to spawn restart script: {:?}", e),
+        }
     }
 
-    fn on_will_open_menu(&self, callback: Box<dyn FnMut()>) {
-        self.0.borrow_mut().will_open_menu = Some(callback);
+    fn activate(&self, ignoring_other_apps: bool) {
+        unsafe {
+            let app = NSApplication::sharedApplication(nil);
+            app.activateIgnoringOtherApps_(ignoring_other_apps.to_objc());
+        }
     }
 
-    fn on_validate_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>) {
-        self.0.borrow_mut().validate_menu_command = Some(callback);
+    fn hide(&self) {
+        unsafe {
+            let app = NSApplication::sharedApplication(nil);
+            let _: () = msg_send![app, hide: nil];
+        }
     }
 
-    fn set_menus(&self, menus: Vec<Menu>, keystroke_matcher: &KeymapMatcher) {
+    fn hide_other_apps(&self) {
         unsafe {
-            let app: id = msg_send![APP_CLASS, sharedApplication];
-            let mut state = self.0.borrow_mut();
-            let actions = &mut state.menu_actions;
-            app.setMainMenu_(self.create_menu_bar(
-                menus,
-                app.delegate(),
-                actions,
-                keystroke_matcher,
-            ));
+            let app = NSApplication::sharedApplication(nil);
+            let _: () = msg_send![app, hideOtherApplications: nil];
+        }
+    }
+
+    fn unhide_other_apps(&self) {
+        unsafe {
+            let app = NSApplication::sharedApplication(nil);
+            let _: () = msg_send![app, unhideAllApplications: nil];
+        }
+    }
+
+    // fn add_status_item(&self, _handle: AnyWindowHandle) -> Box<dyn platform::Window> {
+    //     Box::new(StatusItem::add(self.fonts()))
+    // }
+
+    fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
+        MacDisplay::all()
+            .map(|screen| Rc::new(screen) as Rc<_>)
+            .collect()
+    }
+
+    fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
+        MacDisplay::find_by_id(id).map(|screen| Rc::new(screen) as Rc<_>)
+    }
+
+    fn active_window(&self) -> Option<AnyWindowHandle> {
+        MacWindow::active_window()
+    }
+
+    fn open_window(
+        &self,
+        handle: AnyWindowHandle,
+        options: WindowOptions,
+        draw: Box<dyn FnMut() -> Result<Scene>>,
+    ) -> Box<dyn PlatformWindow> {
+        Box::new(MacWindow::open(
+            handle,
+            options,
+            draw,
+            self.foreground_executor(),
+        ))
+    }
+
+    fn set_display_link_output_callback(
+        &self,
+        display_id: DisplayId,
+        callback: Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp) + Send>,
+    ) {
+        self.0
+            .lock()
+            .display_linker
+            .set_output_callback(display_id, callback);
+    }
+
+    fn start_display_link(&self, display_id: DisplayId) {
+        self.0.lock().display_linker.start(display_id);
+    }
+
+    fn stop_display_link(&self, display_id: DisplayId) {
+        self.0.lock().display_linker.stop(display_id);
+    }
+
+    fn open_url(&self, url: &str) {
+        unsafe {
+            let url = NSURL::alloc(nil)
+                .initWithString_(ns_string(url))
+                .autorelease();
+            let workspace: id = msg_send![class!(NSWorkspace), sharedWorkspace];
+            msg_send![workspace, openURL: url]
         }
     }
 
+    fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>) {
+        self.0.lock().open_urls = Some(callback);
+    }
+
     fn prompt_for_paths(
         &self,
-        options: platform::PathPromptOptions,
+        options: PathPromptOptions,
     ) -> oneshot::Receiver<Option<Vec<PathBuf>>> {
         unsafe {
             let panel = NSOpenPanel::openPanel(nil);
@@ -431,8 +573,8 @@ impl platform::ForegroundPlatform for MacForegroundPlatform {
                     None
                 };
 
-                if let Some(mut done_tx) = done_tx.take() {
-                    let _ = postage::sink::Sink::try_send(&mut done_tx, result);
+                if let Some(done_tx) = done_tx.take() {
+                    let _ = done_tx.send(result);
                 }
             });
             let block = block.copy();
@@ -459,8 +601,8 @@ impl platform::ForegroundPlatform for MacForegroundPlatform {
                     }
                 }
 
-                if let Some(mut done_tx) = done_tx.take() {
-                    let _ = postage::sink::Sink::try_send(&mut done_tx, result);
+                if let Some(done_tx) = done_tx.take() {
+                    let _ = done_tx.send(result);
                 }
             });
             let block = block.copy();
@@ -473,8 +615,8 @@ impl platform::ForegroundPlatform for MacForegroundPlatform {
         unsafe {
             let path = path.to_path_buf();
             self.0
-                .borrow()
-                .foreground
+                .lock()
+                .background_executor
                 .spawn(async move {
                     let full_path = ns_string(path.to_str().unwrap_or(""));
                     let root_full_path = ns_string("");
@@ -488,138 +630,182 @@ impl platform::ForegroundPlatform for MacForegroundPlatform {
                 .detach();
         }
     }
-}
 
-pub struct MacPlatform {
-    dispatcher: Arc<Dispatcher>,
-    fonts: Arc<FontSystem>,
-    pasteboard: id,
-    text_hash_pasteboard_type: id,
-    metadata_pasteboard_type: id,
-}
+    fn on_become_active(&self, callback: Box<dyn FnMut()>) {
+        self.0.lock().become_active = Some(callback);
+    }
 
-impl MacPlatform {
-    pub fn new() -> Self {
-        Self {
-            dispatcher: Arc::new(Dispatcher),
-            fonts: Arc::new(FontSystem::new()),
-            pasteboard: unsafe { NSPasteboard::generalPasteboard(nil) },
-            text_hash_pasteboard_type: unsafe { ns_string("zed-text-hash") },
-            metadata_pasteboard_type: unsafe { ns_string("zed-metadata") },
-        }
+    fn on_resign_active(&self, callback: Box<dyn FnMut()>) {
+        self.0.lock().resign_active = Some(callback);
     }
 
-    unsafe fn read_from_pasteboard(&self, kind: id) -> Option<&[u8]> {
-        let data = self.pasteboard.dataForType(kind);
-        if data == nil {
-            None
-        } else {
-            Some(slice::from_raw_parts(
-                data.bytes() as *mut u8,
-                data.length() as usize,
-            ))
-        }
+    fn on_quit(&self, callback: Box<dyn FnMut()>) {
+        self.0.lock().quit = Some(callback);
     }
-}
 
-unsafe impl Send for MacPlatform {}
-unsafe impl Sync for MacPlatform {}
+    fn on_reopen(&self, callback: Box<dyn FnMut()>) {
+        self.0.lock().reopen = Some(callback);
+    }
 
-impl platform::Platform for MacPlatform {
-    fn dispatcher(&self) -> Arc<dyn platform::Dispatcher> {
-        self.dispatcher.clone()
+    fn on_event(&self, callback: Box<dyn FnMut(InputEvent) -> bool>) {
+        self.0.lock().event = Some(callback);
     }
 
-    fn fonts(&self) -> Arc<dyn platform::FontSystem> {
-        self.fonts.clone()
+    fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>) {
+        self.0.lock().menu_command = Some(callback);
     }
 
-    fn activate(&self, ignoring_other_apps: bool) {
+    fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>) {
+        self.0.lock().will_open_menu = Some(callback);
+    }
+
+    fn on_validate_app_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>) {
+        self.0.lock().validate_menu_command = Some(callback);
+    }
+
+    fn os_name(&self) -> &'static str {
+        "macOS"
+    }
+
+    fn double_click_interval(&self) -> Duration {
         unsafe {
-            let app = NSApplication::sharedApplication(nil);
-            app.activateIgnoringOtherApps_(ignoring_other_apps.to_objc());
+            let double_click_interval: f64 = msg_send![class!(NSEvent), doubleClickInterval];
+            Duration::from_secs_f64(double_click_interval)
         }
     }
 
-    fn hide(&self) {
+    fn os_version(&self) -> Result<SemanticVersion> {
         unsafe {
-            let app = NSApplication::sharedApplication(nil);
-            let _: () = msg_send![app, hide: nil];
+            let process_info = NSProcessInfo::processInfo(nil);
+            let version = process_info.operatingSystemVersion();
+            Ok(SemanticVersion {
+                major: version.majorVersion as usize,
+                minor: version.minorVersion as usize,
+                patch: version.patchVersion as usize,
+            })
         }
     }
 
-    fn hide_other_apps(&self) {
+    fn app_version(&self) -> Result<SemanticVersion> {
         unsafe {
-            let app = NSApplication::sharedApplication(nil);
-            let _: () = msg_send![app, hideOtherApplications: nil];
+            let bundle: id = NSBundle::mainBundle();
+            if bundle.is_null() {
+                Err(anyhow!("app is not running inside a bundle"))
+            } else {
+                let version: id = msg_send![bundle, objectForInfoDictionaryKey: ns_string("CFBundleShortVersionString")];
+                let len = msg_send![version, lengthOfBytesUsingEncoding: NSUTF8StringEncoding];
+                let bytes = version.UTF8String() as *const u8;
+                let version = str::from_utf8(slice::from_raw_parts(bytes, len)).unwrap();
+                version.parse()
+            }
         }
     }
 
-    fn unhide_other_apps(&self) {
+    fn app_path(&self) -> Result<PathBuf> {
         unsafe {
-            let app = NSApplication::sharedApplication(nil);
-            let _: () = msg_send![app, unhideAllApplications: nil];
+            let bundle: id = NSBundle::mainBundle();
+            if bundle.is_null() {
+                Err(anyhow!("app is not running inside a bundle"))
+            } else {
+                Ok(path_from_objc(msg_send![bundle, bundlePath]))
+            }
         }
     }
 
-    fn quit(&self) {
-        // Quitting the app causes us to close windows, which invokes `Window::on_close` callbacks
-        // synchronously before this method terminates. If we call `Platform::quit` while holding a
-        // borrow of the app state (which most of the time we will do), we will end up
-        // double-borrowing the app state in the `on_close` callbacks for our open windows. To solve
-        // this, we make quitting the application asynchronous so that we aren't holding borrows to
-        // the app state on the stack when we actually terminate the app.
-
-        use super::dispatcher::{dispatch_async_f, dispatch_get_main_queue};
-
+    fn set_menus(&self, menus: Vec<Menu>, keymap: &Keymap) {
         unsafe {
-            dispatch_async_f(dispatch_get_main_queue(), ptr::null_mut(), Some(quit));
+            let app: id = msg_send![APP_CLASS, sharedApplication];
+            let mut state = self.0.lock();
+            let actions = &mut state.menu_actions;
+            app.setMainMenu_(self.create_menu_bar(menus, app.delegate(), actions, keymap));
         }
+    }
 
-        unsafe extern "C" fn quit(_: *mut c_void) {
-            let app = NSApplication::sharedApplication(nil);
-            let _: () = msg_send![app, terminate: nil];
+    fn local_timezone(&self) -> UtcOffset {
+        unsafe {
+            let local_timezone: id = msg_send![class!(NSTimeZone), localTimeZone];
+            let seconds_from_gmt: NSInteger = msg_send![local_timezone, secondsFromGMT];
+            UtcOffset::from_whole_seconds(seconds_from_gmt.try_into().unwrap()).unwrap()
         }
     }
 
-    fn screen_by_id(&self, id: uuid::Uuid) -> Option<Rc<dyn platform::Screen>> {
-        Screen::find_by_id(id).map(|screen| Rc::new(screen) as Rc<_>)
+    fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
+        unsafe {
+            let bundle: id = NSBundle::mainBundle();
+            if bundle.is_null() {
+                Err(anyhow!("app is not running inside a bundle"))
+            } else {
+                let name = ns_string(name);
+                let url: id = msg_send![bundle, URLForAuxiliaryExecutable: name];
+                if url.is_null() {
+                    Err(anyhow!("resource not found"))
+                } else {
+                    ns_url_to_path(url)
+                }
+            }
+        }
     }
 
-    fn screens(&self) -> Vec<Rc<dyn platform::Screen>> {
-        Screen::all()
-            .into_iter()
-            .map(|screen| Rc::new(screen) as Rc<_>)
-            .collect()
-    }
+    /// Match cursor style to one of the styles available
+    /// in macOS's [NSCursor](https://developer.apple.com/documentation/appkit/nscursor).
+    fn set_cursor_style(&self, style: CursorStyle) {
+        unsafe {
+            let new_cursor: id = match style {
+                CursorStyle::Arrow => msg_send![class!(NSCursor), arrowCursor],
+                CursorStyle::IBeam => msg_send![class!(NSCursor), IBeamCursor],
+                CursorStyle::Crosshair => msg_send![class!(NSCursor), crosshairCursor],
+                CursorStyle::ClosedHand => msg_send![class!(NSCursor), closedHandCursor],
+                CursorStyle::OpenHand => msg_send![class!(NSCursor), openHandCursor],
+                CursorStyle::PointingHand => msg_send![class!(NSCursor), pointingHandCursor],
+                CursorStyle::ResizeLeft => msg_send![class!(NSCursor), resizeLeftCursor],
+                CursorStyle::ResizeRight => msg_send![class!(NSCursor), resizeRightCursor],
+                CursorStyle::ResizeLeftRight => msg_send![class!(NSCursor), resizeLeftRightCursor],
+                CursorStyle::ResizeUp => msg_send![class!(NSCursor), resizeUpCursor],
+                CursorStyle::ResizeDown => msg_send![class!(NSCursor), resizeDownCursor],
+                CursorStyle::ResizeUpDown => msg_send![class!(NSCursor), resizeUpDownCursor],
+                CursorStyle::DisappearingItem => {
+                    msg_send![class!(NSCursor), disappearingItemCursor]
+                }
+                CursorStyle::IBeamCursorForVerticalLayout => {
+                    msg_send![class!(NSCursor), IBeamCursorForVerticalLayout]
+                }
+                CursorStyle::OperationNotAllowed => {
+                    msg_send![class!(NSCursor), operationNotAllowedCursor]
+                }
+                CursorStyle::DragLink => msg_send![class!(NSCursor), dragLinkCursor],
+                CursorStyle::DragCopy => msg_send![class!(NSCursor), dragCopyCursor],
+                CursorStyle::ContextualMenu => msg_send![class!(NSCursor), contextualMenuCursor],
+            };
 
-    fn open_window(
-        &self,
-        handle: AnyWindowHandle,
-        options: platform::WindowOptions,
-        executor: Rc<executor::Foreground>,
-    ) -> Box<dyn platform::Window> {
-        Box::new(MacWindow::open(handle, options, executor, self.fonts()))
+            let old_cursor: id = msg_send![class!(NSCursor), currentCursor];
+            if new_cursor != old_cursor {
+                let _: () = msg_send![new_cursor, set];
+            }
+        }
     }
 
-    fn main_window(&self) -> Option<AnyWindowHandle> {
-        MacWindow::main_window()
-    }
+    fn should_auto_hide_scrollbars(&self) -> bool {
+        #[allow(non_upper_case_globals)]
+        const NSScrollerStyleOverlay: NSInteger = 1;
 
-    fn add_status_item(&self, _handle: AnyWindowHandle) -> Box<dyn platform::Window> {
-        Box::new(StatusItem::add(self.fonts()))
+        unsafe {
+            let style: NSInteger = msg_send![class!(NSScroller), preferredScrollerStyle];
+            style == NSScrollerStyleOverlay
+        }
     }
 
     fn write_to_clipboard(&self, item: ClipboardItem) {
+        let state = self.0.lock();
         unsafe {
-            self.pasteboard.clearContents();
+            state.pasteboard.clearContents();
 
             let text_bytes = NSData::dataWithBytes_length_(
                 nil,
                 item.text.as_ptr() as *const c_void,
                 item.text.len() as u64,
             );
-            self.pasteboard
+            state
+                .pasteboard
                 .setData_forType(text_bytes, NSPasteboardTypeString);
 
             if let Some(metadata) = item.metadata.as_ref() {
@@ -629,30 +815,35 @@ impl platform::Platform for MacPlatform {
                     hash_bytes.as_ptr() as *const c_void,
                     hash_bytes.len() as u64,
                 );
-                self.pasteboard
-                    .setData_forType(hash_bytes, self.text_hash_pasteboard_type);
+                state
+                    .pasteboard
+                    .setData_forType(hash_bytes, state.text_hash_pasteboard_type);
 
                 let metadata_bytes = NSData::dataWithBytes_length_(
                     nil,
                     metadata.as_ptr() as *const c_void,
                     metadata.len() as u64,
                 );
-                self.pasteboard
-                    .setData_forType(metadata_bytes, self.metadata_pasteboard_type);
+                state
+                    .pasteboard
+                    .setData_forType(metadata_bytes, state.metadata_pasteboard_type);
             }
         }
     }
 
     fn read_from_clipboard(&self) -> Option<ClipboardItem> {
+        let state = self.0.lock();
         unsafe {
-            if let Some(text_bytes) = self.read_from_pasteboard(NSPasteboardTypeString) {
+            if let Some(text_bytes) =
+                self.read_from_pasteboard(state.pasteboard, NSPasteboardTypeString)
+            {
                 let text = String::from_utf8_lossy(text_bytes).to_string();
                 let hash_bytes = self
-                    .read_from_pasteboard(self.text_hash_pasteboard_type)
+                    .read_from_pasteboard(state.pasteboard, state.text_hash_pasteboard_type)
                     .and_then(|bytes| bytes.try_into().ok())
                     .map(u64::from_be_bytes);
                 let metadata_bytes = self
-                    .read_from_pasteboard(self.metadata_pasteboard_type)
+                    .read_from_pasteboard(state.pasteboard, state.metadata_pasteboard_type)
                     .and_then(|bytes| String::from_utf8(bytes.to_vec()).ok());
 
                 if let Some((hash, metadata)) = hash_bytes.zip(metadata_bytes) {
@@ -679,16 +870,6 @@ impl platform::Platform for MacPlatform {
         }
     }
 
-    fn open_url(&self, url: &str) {
-        unsafe {
-            let url = NSURL::alloc(nil)
-                .initWithString_(ns_string(url))
-                .autorelease();
-            let workspace: id = msg_send![class!(NSWorkspace), sharedWorkspace];
-            msg_send![workspace, openURL: url]
-        }
-    }
-
     fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Result<()> {
         let url = CFString::from(url);
         let username = CFString::from(username);
@@ -788,137 +969,6 @@ impl platform::Platform for MacPlatform {
         }
         Ok(())
     }
-
-    fn set_cursor_style(&self, style: CursorStyle) {
-        unsafe {
-            let new_cursor: id = match style {
-                CursorStyle::Arrow => msg_send![class!(NSCursor), arrowCursor],
-                CursorStyle::ResizeLeftRight => {
-                    msg_send![class!(NSCursor), resizeLeftRightCursor]
-                }
-                CursorStyle::ResizeUpDown => msg_send![class!(NSCursor), resizeUpDownCursor],
-                CursorStyle::PointingHand => msg_send![class!(NSCursor), pointingHandCursor],
-                CursorStyle::IBeam => msg_send![class!(NSCursor), IBeamCursor],
-            };
-
-            let old_cursor: id = msg_send![class!(NSCursor), currentCursor];
-            if new_cursor != old_cursor {
-                let _: () = msg_send![new_cursor, set];
-            }
-        }
-    }
-
-    fn should_auto_hide_scrollbars(&self) -> bool {
-        #[allow(non_upper_case_globals)]
-        const NSScrollerStyleOverlay: NSInteger = 1;
-
-        unsafe {
-            let style: NSInteger = msg_send![class!(NSScroller), preferredScrollerStyle];
-            style == NSScrollerStyleOverlay
-        }
-    }
-
-    fn local_timezone(&self) -> UtcOffset {
-        unsafe {
-            let local_timezone: id = msg_send![class!(NSTimeZone), localTimeZone];
-            let seconds_from_gmt: NSInteger = msg_send![local_timezone, secondsFromGMT];
-            UtcOffset::from_whole_seconds(seconds_from_gmt.try_into().unwrap()).unwrap()
-        }
-    }
-
-    fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
-        unsafe {
-            let bundle: id = NSBundle::mainBundle();
-            if bundle.is_null() {
-                Err(anyhow!("app is not running inside a bundle"))
-            } else {
-                let name = ns_string(name);
-                let url: id = msg_send![bundle, URLForAuxiliaryExecutable: name];
-                if url.is_null() {
-                    Err(anyhow!("resource not found"))
-                } else {
-                    ns_url_to_path(url)
-                }
-            }
-        }
-    }
-
-    fn app_path(&self) -> Result<PathBuf> {
-        unsafe {
-            let bundle: id = NSBundle::mainBundle();
-            if bundle.is_null() {
-                Err(anyhow!("app is not running inside a bundle"))
-            } else {
-                Ok(path_from_objc(msg_send![bundle, bundlePath]))
-            }
-        }
-    }
-
-    fn app_version(&self) -> Result<platform::AppVersion> {
-        unsafe {
-            let bundle: id = NSBundle::mainBundle();
-            if bundle.is_null() {
-                Err(anyhow!("app is not running inside a bundle"))
-            } else {
-                let version: id = msg_send![bundle, objectForInfoDictionaryKey: ns_string("CFBundleShortVersionString")];
-                let len = msg_send![version, lengthOfBytesUsingEncoding: NSUTF8StringEncoding];
-                let bytes = version.UTF8String() as *const u8;
-                let version = str::from_utf8(slice::from_raw_parts(bytes, len)).unwrap();
-                version.parse()
-            }
-        }
-    }
-
-    fn os_name(&self) -> &'static str {
-        "macOS"
-    }
-
-    fn os_version(&self) -> Result<crate::platform::AppVersion> {
-        unsafe {
-            let process_info = NSProcessInfo::processInfo(nil);
-            let version = process_info.operatingSystemVersion();
-            Ok(AppVersion {
-                major: version.majorVersion as usize,
-                minor: version.minorVersion as usize,
-                patch: version.patchVersion as usize,
-            })
-        }
-    }
-
-    fn restart(&self) {
-        use std::os::unix::process::CommandExt as _;
-
-        let app_pid = std::process::id().to_string();
-        let app_path = self
-            .app_path()
-            .ok()
-            // When the app is not bundled, `app_path` returns the
-            // directory containing the executable. Disregard this
-            // and get the path to the executable itself.
-            .and_then(|path| (path.extension()?.to_str()? == "app").then_some(path))
-            .unwrap_or_else(|| std::env::current_exe().unwrap());
-
-        // Wait until this process has exited and then re-open this path.
-        let script = r#"
-            while kill -0 $0 2> /dev/null; do
-                sleep 0.1
-            done
-            open "$1"
-        "#;
-
-        let restart_process = Command::new("/bin/bash")
-            .arg("-c")
-            .arg(script)
-            .arg(app_pid)
-            .arg(app_path)
-            .process_group(0)
-            .spawn();
-
-        match restart_process {
-            Ok(_) => self.quit(),
-            Err(e) => log::error!("failed to spawn restart script: {:?}", e),
-        }
-    }
 }
 
 unsafe fn path_from_objc(path: id) -> PathBuf {
@@ -928,18 +978,18 @@ unsafe fn path_from_objc(path: id) -> PathBuf {
     PathBuf::from(path)
 }
 
-unsafe fn get_foreground_platform(object: &mut Object) -> &MacForegroundPlatform {
+unsafe fn get_mac_platform(object: &mut Object) -> &MacPlatform {
     let platform_ptr: *mut c_void = *object.get_ivar(MAC_PLATFORM_IVAR);
     assert!(!platform_ptr.is_null());
-    &*(platform_ptr as *const MacForegroundPlatform)
+    &*(platform_ptr as *const MacPlatform)
 }
 
 extern "C" fn send_event(this: &mut Object, _sel: Sel, native_event: id) {
     unsafe {
-        if let Some(event) = Event::from_native(native_event, None) {
-            let platform = get_foreground_platform(this);
-            if let Some(callback) = platform.0.borrow_mut().event.as_mut() {
-                if callback(event) {
+        if let Some(event) = InputEvent::from_native(native_event, None) {
+            let platform = get_mac_platform(this);
+            if let Some(callback) = platform.0.lock().event.as_mut() {
+                if !callback(event) {
                     return;
                 }
             }
@@ -953,8 +1003,8 @@ extern "C" fn did_finish_launching(this: &mut Object, _: Sel, _: id) {
         let app: id = msg_send![APP_CLASS, sharedApplication];
         app.setActivationPolicy_(NSApplicationActivationPolicyRegular);
 
-        let platform = get_foreground_platform(this);
-        let callback = platform.0.borrow_mut().finish_launching.take();
+        let platform = get_mac_platform(this);
+        let callback = platform.0.lock().finish_launching.take();
         if let Some(callback) = callback {
             callback();
         }
@@ -963,30 +1013,30 @@ extern "C" fn did_finish_launching(this: &mut Object, _: Sel, _: id) {
 
 extern "C" fn should_handle_reopen(this: &mut Object, _: Sel, _: id, has_open_windows: bool) {
     if !has_open_windows {
-        let platform = unsafe { get_foreground_platform(this) };
-        if let Some(callback) = platform.0.borrow_mut().reopen.as_mut() {
+        let platform = unsafe { get_mac_platform(this) };
+        if let Some(callback) = platform.0.lock().reopen.as_mut() {
             callback();
         }
     }
 }
 
 extern "C" fn did_become_active(this: &mut Object, _: Sel, _: id) {
-    let platform = unsafe { get_foreground_platform(this) };
-    if let Some(callback) = platform.0.borrow_mut().become_active.as_mut() {
+    let platform = unsafe { get_mac_platform(this) };
+    if let Some(callback) = platform.0.lock().become_active.as_mut() {
         callback();
     }
 }
 
 extern "C" fn did_resign_active(this: &mut Object, _: Sel, _: id) {
-    let platform = unsafe { get_foreground_platform(this) };
-    if let Some(callback) = platform.0.borrow_mut().resign_active.as_mut() {
+    let platform = unsafe { get_mac_platform(this) };
+    if let Some(callback) = platform.0.lock().resign_active.as_mut() {
         callback();
     }
 }
 
 extern "C" fn will_terminate(this: &mut Object, _: Sel, _: id) {
-    let platform = unsafe { get_foreground_platform(this) };
-    if let Some(callback) = platform.0.borrow_mut().quit.as_mut() {
+    let platform = unsafe { get_mac_platform(this) };
+    if let Some(callback) = platform.0.lock().quit.as_mut() {
         callback();
     }
 }

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

@@ -1,1277 +0,0 @@
-use super::{atlas::AtlasAllocator, image_cache::ImageCache, sprite_cache::SpriteCache};
-use crate::{
-    color::Color,
-    geometry::{
-        rect::RectF,
-        vector::{vec2f, vec2i, Vector2F},
-    },
-    platform,
-    scene::{Glyph, Icon, Image, ImageGlyph, Layer, Quad, Scene, Shadow, Underline},
-};
-use cocoa::{
-    base::{NO, YES},
-    foundation::NSUInteger,
-    quartzcore::AutoresizingMask,
-};
-use core_foundation::base::TCFType;
-use foreign_types::ForeignTypeRef;
-use log::warn;
-use media::core_video::{self, CVMetalTextureCache};
-use metal::{CommandQueue, MTLPixelFormat, MTLResourceOptions, NSRange};
-use objc::{self, msg_send, sel, sel_impl};
-use shaders::ToFloat2 as _;
-use std::{collections::HashMap, ffi::c_void, iter::Peekable, mem, ptr, sync::Arc, vec};
-
-const SHADERS_METALLIB: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib"));
-const INSTANCE_BUFFER_SIZE: usize = 8192 * 1024; // This is an arbitrary decision. There's probably a more optimal value.
-
-pub struct Renderer {
-    layer: metal::MetalLayer,
-    command_queue: CommandQueue,
-    sprite_cache: SpriteCache,
-    image_cache: ImageCache,
-    path_atlases: AtlasAllocator,
-    quad_pipeline_state: metal::RenderPipelineState,
-    shadow_pipeline_state: metal::RenderPipelineState,
-    sprite_pipeline_state: metal::RenderPipelineState,
-    image_pipeline_state: metal::RenderPipelineState,
-    surface_pipeline_state: metal::RenderPipelineState,
-    path_atlas_pipeline_state: metal::RenderPipelineState,
-    underline_pipeline_state: metal::RenderPipelineState,
-    unit_vertices: metal::Buffer,
-    instances: metal::Buffer,
-    cv_texture_cache: core_video::CVMetalTextureCache,
-}
-
-struct PathSprite {
-    layer_id: usize,
-    atlas_id: usize,
-    shader_data: shaders::GPUISprite,
-}
-
-pub struct Surface {
-    pub bounds: RectF,
-    pub image_buffer: core_video::CVImageBuffer,
-}
-
-impl Renderer {
-    pub fn new(is_opaque: bool, fonts: Arc<dyn platform::FontSystem>) -> Self {
-        const PIXEL_FORMAT: MTLPixelFormat = MTLPixelFormat::BGRA8Unorm;
-
-        let device: metal::Device = if let Some(device) = metal::Device::system_default() {
-            device
-        } else {
-            log::error!("unable to access a compatible graphics device");
-            std::process::exit(1);
-        };
-
-        let layer = metal::MetalLayer::new();
-        layer.set_device(&device);
-        layer.set_pixel_format(PIXEL_FORMAT);
-        layer.set_presents_with_transaction(true);
-        layer.set_opaque(is_opaque);
-        unsafe {
-            let _: () = msg_send![&*layer, setAllowsNextDrawableTimeout: NO];
-            let _: () = msg_send![&*layer, setNeedsDisplayOnBoundsChange: YES];
-            let _: () = msg_send![
-                &*layer,
-                setAutoresizingMask: AutoresizingMask::WIDTH_SIZABLE
-                    | AutoresizingMask::HEIGHT_SIZABLE
-            ];
-        }
-
-        let library = device
-            .new_library_with_data(SHADERS_METALLIB)
-            .expect("error building metal library");
-
-        let unit_vertices = [
-            (0., 0.).to_float2(),
-            (1., 0.).to_float2(),
-            (0., 1.).to_float2(),
-            (0., 1.).to_float2(),
-            (1., 0.).to_float2(),
-            (1., 1.).to_float2(),
-        ];
-        let unit_vertices = device.new_buffer_with_data(
-            unit_vertices.as_ptr() as *const c_void,
-            (unit_vertices.len() * mem::size_of::<shaders::vector_float2>()) as u64,
-            MTLResourceOptions::StorageModeManaged,
-        );
-        let instances = device.new_buffer(
-            INSTANCE_BUFFER_SIZE as u64,
-            MTLResourceOptions::StorageModeManaged,
-        );
-
-        let sprite_cache = SpriteCache::new(device.clone(), vec2i(1024, 768), 1., fonts.clone());
-        let image_cache = ImageCache::new(device.clone(), vec2i(1024, 768), 1., fonts);
-        let path_atlases =
-            AtlasAllocator::new(device.clone(), build_path_atlas_texture_descriptor());
-        let quad_pipeline_state = build_pipeline_state(
-            &device,
-            &library,
-            "quad",
-            "quad_vertex",
-            "quad_fragment",
-            PIXEL_FORMAT,
-        );
-        let shadow_pipeline_state = build_pipeline_state(
-            &device,
-            &library,
-            "shadow",
-            "shadow_vertex",
-            "shadow_fragment",
-            PIXEL_FORMAT,
-        );
-        let sprite_pipeline_state = build_pipeline_state(
-            &device,
-            &library,
-            "sprite",
-            "sprite_vertex",
-            "sprite_fragment",
-            PIXEL_FORMAT,
-        );
-        let image_pipeline_state = build_pipeline_state(
-            &device,
-            &library,
-            "image",
-            "image_vertex",
-            "image_fragment",
-            PIXEL_FORMAT,
-        );
-        let surface_pipeline_state = build_pipeline_state(
-            &device,
-            &library,
-            "surface",
-            "surface_vertex",
-            "surface_fragment",
-            PIXEL_FORMAT,
-        );
-        let path_atlas_pipeline_state = build_path_atlas_pipeline_state(
-            &device,
-            &library,
-            "path_atlas",
-            "path_atlas_vertex",
-            "path_atlas_fragment",
-            MTLPixelFormat::R16Float,
-        );
-        let underline_pipeline_state = build_pipeline_state(
-            &device,
-            &library,
-            "underline",
-            "underline_vertex",
-            "underline_fragment",
-            PIXEL_FORMAT,
-        );
-        let cv_texture_cache = unsafe { CVMetalTextureCache::new(device.as_ptr()).unwrap() };
-        Self {
-            layer,
-            command_queue: device.new_command_queue(),
-            sprite_cache,
-            image_cache,
-            path_atlases,
-            quad_pipeline_state,
-            shadow_pipeline_state,
-            sprite_pipeline_state,
-            image_pipeline_state,
-            surface_pipeline_state,
-            path_atlas_pipeline_state,
-            underline_pipeline_state,
-            unit_vertices,
-            instances,
-            cv_texture_cache,
-        }
-    }
-
-    pub fn layer(&self) -> &metal::MetalLayerRef {
-        &*self.layer
-    }
-
-    pub fn render(&mut self, scene: &Scene) {
-        let layer = self.layer.clone();
-        let drawable_size = layer.drawable_size();
-        let drawable = if let Some(drawable) = layer.next_drawable() {
-            drawable
-        } else {
-            log::error!(
-                "failed to retrieve next drawable, drawable size: {:?}",
-                drawable_size
-            );
-            return;
-        };
-        let command_queue = self.command_queue.clone();
-        let command_buffer = command_queue.new_command_buffer();
-
-        self.sprite_cache.set_scale_factor(scene.scale_factor());
-        self.image_cache.set_scale_factor(scene.scale_factor());
-
-        let mut offset = 0;
-
-        let path_sprites = self.render_path_atlases(scene, &mut offset, command_buffer);
-        self.render_layers(
-            scene,
-            path_sprites,
-            &mut offset,
-            vec2f(drawable_size.width as f32, drawable_size.height as f32),
-            command_buffer,
-            drawable.texture(),
-        );
-        self.instances.did_modify_range(NSRange {
-            location: 0,
-            length: offset as NSUInteger,
-        });
-        self.image_cache.finish_frame();
-
-        command_buffer.commit();
-        command_buffer.wait_until_completed();
-        drawable.present();
-    }
-
-    fn render_path_atlases(
-        &mut self,
-        scene: &Scene,
-        offset: &mut usize,
-        command_buffer: &metal::CommandBufferRef,
-    ) -> Vec<PathSprite> {
-        self.path_atlases.clear();
-        let mut sprites = Vec::new();
-        let mut vertices = Vec::<shaders::GPUIPathVertex>::new();
-        let mut current_atlas_id = None;
-        for (layer_id, layer) in scene.layers().enumerate() {
-            for path in layer.paths() {
-                let origin = path.bounds.origin() * scene.scale_factor();
-                let size = (path.bounds.size() * scene.scale_factor()).ceil();
-
-                let path_allocation = self.path_atlases.allocate(size.to_i32());
-                if path_allocation.is_none() {
-                    // Path size was likely zero.
-                    warn!("could not allocate path texture of size {:?}", size);
-                    continue;
-                }
-                let (alloc_id, atlas_origin) = path_allocation.unwrap();
-                let atlas_origin = atlas_origin.to_f32();
-                sprites.push(PathSprite {
-                    layer_id,
-                    atlas_id: alloc_id.atlas_id,
-                    shader_data: shaders::GPUISprite {
-                        origin: origin.floor().to_float2(),
-                        target_size: size.to_float2(),
-                        source_size: size.to_float2(),
-                        atlas_origin: atlas_origin.to_float2(),
-                        color: path.color.to_uchar4(),
-                        compute_winding: 1,
-                    },
-                });
-
-                if let Some(current_atlas_id) = current_atlas_id {
-                    if alloc_id.atlas_id != current_atlas_id {
-                        self.render_paths_to_atlas(
-                            offset,
-                            &vertices,
-                            current_atlas_id,
-                            command_buffer,
-                        );
-                        vertices.clear();
-                    }
-                }
-
-                current_atlas_id = Some(alloc_id.atlas_id);
-
-                for vertex in &path.vertices {
-                    let xy_position =
-                        (vertex.xy_position - path.bounds.origin()) * scene.scale_factor();
-                    vertices.push(shaders::GPUIPathVertex {
-                        xy_position: (atlas_origin + xy_position).to_float2(),
-                        st_position: vertex.st_position.to_float2(),
-                        clip_rect_origin: atlas_origin.to_float2(),
-                        clip_rect_size: size.to_float2(),
-                    });
-                }
-            }
-        }
-
-        if let Some(atlas_id) = current_atlas_id {
-            self.render_paths_to_atlas(offset, &vertices, atlas_id, command_buffer);
-        }
-
-        sprites
-    }
-
-    fn render_paths_to_atlas(
-        &mut self,
-        offset: &mut usize,
-        vertices: &[shaders::GPUIPathVertex],
-        atlas_id: usize,
-        command_buffer: &metal::CommandBufferRef,
-    ) {
-        align_offset(offset);
-        let next_offset = *offset + vertices.len() * mem::size_of::<shaders::GPUIPathVertex>();
-        assert!(
-            next_offset <= INSTANCE_BUFFER_SIZE,
-            "instance buffer exhausted"
-        );
-
-        let render_pass_descriptor = metal::RenderPassDescriptor::new();
-        let color_attachment = render_pass_descriptor
-            .color_attachments()
-            .object_at(0)
-            .unwrap();
-        let texture = self.path_atlases.texture(atlas_id).unwrap();
-        color_attachment.set_texture(Some(texture));
-        color_attachment.set_load_action(metal::MTLLoadAction::Clear);
-        color_attachment.set_store_action(metal::MTLStoreAction::Store);
-        color_attachment.set_clear_color(metal::MTLClearColor::new(0., 0., 0., 1.));
-
-        let path_atlas_command_encoder =
-            command_buffer.new_render_command_encoder(render_pass_descriptor);
-        path_atlas_command_encoder.set_render_pipeline_state(&self.path_atlas_pipeline_state);
-        path_atlas_command_encoder.set_vertex_buffer(
-            shaders::GPUIPathAtlasVertexInputIndex_GPUIPathAtlasVertexInputIndexVertices as u64,
-            Some(&self.instances),
-            *offset as u64,
-        );
-        path_atlas_command_encoder.set_vertex_bytes(
-            shaders::GPUIPathAtlasVertexInputIndex_GPUIPathAtlasVertexInputIndexAtlasSize as u64,
-            mem::size_of::<shaders::vector_float2>() as u64,
-            [vec2i(texture.width() as i32, texture.height() as i32).to_float2()].as_ptr()
-                as *const c_void,
-        );
-
-        let buffer_contents = unsafe {
-            (self.instances.contents() as *mut u8).add(*offset) as *mut shaders::GPUIPathVertex
-        };
-
-        for (ix, vertex) in vertices.iter().enumerate() {
-            unsafe {
-                *buffer_contents.add(ix) = *vertex;
-            }
-        }
-
-        path_atlas_command_encoder.draw_primitives(
-            metal::MTLPrimitiveType::Triangle,
-            0,
-            vertices.len() as u64,
-        );
-        path_atlas_command_encoder.end_encoding();
-        *offset = next_offset;
-    }
-
-    fn render_layers(
-        &mut self,
-        scene: &Scene,
-        path_sprites: Vec<PathSprite>,
-        offset: &mut usize,
-        drawable_size: Vector2F,
-        command_buffer: &metal::CommandBufferRef,
-        output: &metal::TextureRef,
-    ) {
-        let render_pass_descriptor = metal::RenderPassDescriptor::new();
-        let color_attachment = render_pass_descriptor
-            .color_attachments()
-            .object_at(0)
-            .unwrap();
-        color_attachment.set_texture(Some(output));
-        color_attachment.set_load_action(metal::MTLLoadAction::Clear);
-        color_attachment.set_store_action(metal::MTLStoreAction::Store);
-        let alpha = if self.layer.is_opaque() { 1. } else { 0. };
-        color_attachment.set_clear_color(metal::MTLClearColor::new(0., 0., 0., alpha));
-        let command_encoder = command_buffer.new_render_command_encoder(render_pass_descriptor);
-
-        command_encoder.set_viewport(metal::MTLViewport {
-            originX: 0.0,
-            originY: 0.0,
-            width: drawable_size.x() as f64,
-            height: drawable_size.y() as f64,
-            znear: 0.0,
-            zfar: 1.0,
-        });
-
-        let scale_factor = scene.scale_factor();
-        let mut path_sprites = path_sprites.into_iter().peekable();
-        for (layer_id, layer) in scene.layers().enumerate() {
-            self.clip(scene, layer, drawable_size, command_encoder);
-            self.render_shadows(
-                layer.shadows(),
-                scale_factor,
-                offset,
-                drawable_size,
-                command_encoder,
-            );
-            self.render_quads(
-                layer.quads(),
-                scale_factor,
-                offset,
-                drawable_size,
-                command_encoder,
-            );
-            self.render_path_sprites(
-                layer_id,
-                &mut path_sprites,
-                offset,
-                drawable_size,
-                command_encoder,
-            );
-            self.render_underlines(
-                layer.underlines(),
-                scale_factor,
-                offset,
-                drawable_size,
-                command_encoder,
-            );
-            self.render_sprites(
-                layer.glyphs(),
-                layer.icons(),
-                scale_factor,
-                offset,
-                drawable_size,
-                command_encoder,
-            );
-            self.render_images(
-                layer.images(),
-                layer.image_glyphs(),
-                scale_factor,
-                offset,
-                drawable_size,
-                command_encoder,
-            );
-            self.render_surfaces(
-                layer.surfaces(),
-                scale_factor,
-                offset,
-                drawable_size,
-                command_encoder,
-            );
-        }
-
-        command_encoder.end_encoding();
-    }
-
-    fn clip(
-        &mut self,
-        scene: &Scene,
-        layer: &Layer,
-        drawable_size: Vector2F,
-        command_encoder: &metal::RenderCommandEncoderRef,
-    ) {
-        let clip_bounds = (layer
-            .clip_bounds()
-            .unwrap_or_else(|| RectF::new(vec2f(0., 0.), drawable_size / scene.scale_factor()))
-            * scene.scale_factor())
-        .round();
-        command_encoder.set_scissor_rect(metal::MTLScissorRect {
-            x: clip_bounds.origin_x() as NSUInteger,
-            y: clip_bounds.origin_y() as NSUInteger,
-            width: clip_bounds.width() as NSUInteger,
-            height: clip_bounds.height() as NSUInteger,
-        });
-    }
-
-    fn render_shadows(
-        &mut self,
-        shadows: &[Shadow],
-        scale_factor: f32,
-        offset: &mut usize,
-        drawable_size: Vector2F,
-        command_encoder: &metal::RenderCommandEncoderRef,
-    ) {
-        if shadows.is_empty() {
-            return;
-        }
-
-        align_offset(offset);
-        let next_offset = *offset + shadows.len() * mem::size_of::<shaders::GPUIShadow>();
-        assert!(
-            next_offset <= INSTANCE_BUFFER_SIZE,
-            "instance buffer exhausted"
-        );
-
-        command_encoder.set_render_pipeline_state(&self.shadow_pipeline_state);
-        command_encoder.set_vertex_buffer(
-            shaders::GPUIShadowInputIndex_GPUIShadowInputIndexVertices as u64,
-            Some(&self.unit_vertices),
-            0,
-        );
-        command_encoder.set_vertex_buffer(
-            shaders::GPUIShadowInputIndex_GPUIShadowInputIndexShadows as u64,
-            Some(&self.instances),
-            *offset as u64,
-        );
-        command_encoder.set_vertex_bytes(
-            shaders::GPUIShadowInputIndex_GPUIShadowInputIndexUniforms as u64,
-            mem::size_of::<shaders::GPUIUniforms>() as u64,
-            [shaders::GPUIUniforms {
-                viewport_size: drawable_size.to_float2(),
-            }]
-            .as_ptr() as *const c_void,
-        );
-
-        let buffer_contents = unsafe {
-            (self.instances.contents() as *mut u8).add(*offset) as *mut shaders::GPUIShadow
-        };
-        for (ix, shadow) in shadows.iter().enumerate() {
-            let shape_bounds = shadow.bounds * scale_factor;
-            let corner_radii = shadow.corner_radii * scale_factor;
-            let shader_shadow = shaders::GPUIShadow {
-                origin: shape_bounds.origin().to_float2(),
-                size: shape_bounds.size().to_float2(),
-                corner_radius_top_left: corner_radii.top_left,
-                corner_radius_top_right: corner_radii.top_right,
-                corner_radius_bottom_right: corner_radii.bottom_right,
-                corner_radius_bottom_left: corner_radii.bottom_left,
-                sigma: shadow.sigma,
-                color: shadow.color.to_uchar4(),
-            };
-            unsafe {
-                *(buffer_contents.add(ix)) = shader_shadow;
-            }
-        }
-
-        command_encoder.draw_primitives_instanced(
-            metal::MTLPrimitiveType::Triangle,
-            0,
-            6,
-            shadows.len() as u64,
-        );
-        *offset = next_offset;
-    }
-
-    fn render_quads(
-        &mut self,
-        quads: &[Quad],
-        scale_factor: f32,
-        offset: &mut usize,
-        drawable_size: Vector2F,
-        command_encoder: &metal::RenderCommandEncoderRef,
-    ) {
-        if quads.is_empty() {
-            return;
-        }
-        align_offset(offset);
-        let next_offset = *offset + quads.len() * mem::size_of::<shaders::GPUIQuad>();
-        assert!(
-            next_offset <= INSTANCE_BUFFER_SIZE,
-            "instance buffer exhausted"
-        );
-
-        command_encoder.set_render_pipeline_state(&self.quad_pipeline_state);
-        command_encoder.set_vertex_buffer(
-            shaders::GPUIQuadInputIndex_GPUIQuadInputIndexVertices as u64,
-            Some(&self.unit_vertices),
-            0,
-        );
-        command_encoder.set_vertex_buffer(
-            shaders::GPUIQuadInputIndex_GPUIQuadInputIndexQuads as u64,
-            Some(&self.instances),
-            *offset as u64,
-        );
-        command_encoder.set_vertex_bytes(
-            shaders::GPUIQuadInputIndex_GPUIQuadInputIndexUniforms as u64,
-            mem::size_of::<shaders::GPUIUniforms>() as u64,
-            [shaders::GPUIUniforms {
-                viewport_size: drawable_size.to_float2(),
-            }]
-            .as_ptr() as *const c_void,
-        );
-
-        let buffer_contents = unsafe {
-            (self.instances.contents() as *mut u8).add(*offset) as *mut shaders::GPUIQuad
-        };
-        for (ix, quad) in quads.iter().enumerate() {
-            let bounds = quad.bounds * scale_factor;
-            let shader_quad = shaders::GPUIQuad {
-                origin: bounds.origin().round().to_float2(),
-                size: bounds.size().round().to_float2(),
-                background_color: quad
-                    .background
-                    .unwrap_or_else(Color::transparent_black)
-                    .to_uchar4(),
-                border_top: quad.border.top * scale_factor,
-                border_right: quad.border.right * scale_factor,
-                border_bottom: quad.border.bottom * scale_factor,
-                border_left: quad.border.left * scale_factor,
-                border_color: quad.border.color.to_uchar4(),
-                corner_radius_top_left: quad.corner_radii.top_left * scale_factor,
-                corner_radius_top_right: quad.corner_radii.top_right * scale_factor,
-                corner_radius_bottom_right: quad.corner_radii.bottom_right * scale_factor,
-                corner_radius_bottom_left: quad.corner_radii.bottom_left * scale_factor,
-            };
-            unsafe {
-                *(buffer_contents.add(ix)) = shader_quad;
-            }
-        }
-
-        command_encoder.draw_primitives_instanced(
-            metal::MTLPrimitiveType::Triangle,
-            0,
-            6,
-            quads.len() as u64,
-        );
-        *offset = next_offset;
-    }
-
-    fn render_sprites(
-        &mut self,
-        glyphs: &[Glyph],
-        icons: &[Icon],
-        scale_factor: f32,
-        offset: &mut usize,
-        drawable_size: Vector2F,
-        command_encoder: &metal::RenderCommandEncoderRef,
-    ) {
-        if glyphs.is_empty() && icons.is_empty() {
-            return;
-        }
-
-        let mut sprites_by_atlas = HashMap::new();
-
-        for glyph in glyphs {
-            if let Some(sprite) = self.sprite_cache.render_glyph(
-                glyph.font_id,
-                glyph.font_size,
-                glyph.id,
-                glyph.origin,
-            ) {
-                // Snap sprite to pixel grid.
-                let origin = (glyph.origin * scale_factor).floor() + sprite.offset.to_f32();
-
-                sprites_by_atlas
-                    .entry(sprite.atlas_id)
-                    .or_insert_with(Vec::new)
-                    .push(shaders::GPUISprite {
-                        origin: origin.to_float2(),
-                        target_size: sprite.size.to_float2(),
-                        source_size: sprite.size.to_float2(),
-                        atlas_origin: sprite.atlas_origin.to_float2(),
-                        color: glyph.color.to_uchar4(),
-                        compute_winding: 0,
-                    });
-            }
-        }
-
-        for icon in icons {
-            // Snap sprite to pixel grid.
-            let origin = (icon.bounds.origin() * scale_factor).floor();
-            let target_size = (icon.bounds.size() * scale_factor).ceil();
-            let source_size = (target_size * 2.).to_i32();
-
-            let sprite =
-                self.sprite_cache
-                    .render_icon(source_size, icon.path.clone(), icon.svg.clone());
-            if sprite.is_none() {
-                continue;
-            }
-            let sprite = sprite.unwrap();
-
-            sprites_by_atlas
-                .entry(sprite.atlas_id)
-                .or_insert_with(Vec::new)
-                .push(shaders::GPUISprite {
-                    origin: origin.to_float2(),
-                    target_size: target_size.to_float2(),
-                    source_size: sprite.size.to_float2(),
-                    atlas_origin: sprite.atlas_origin.to_float2(),
-                    color: icon.color.to_uchar4(),
-                    compute_winding: 0,
-                });
-        }
-
-        command_encoder.set_render_pipeline_state(&self.sprite_pipeline_state);
-        command_encoder.set_vertex_buffer(
-            shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexVertices as u64,
-            Some(&self.unit_vertices),
-            0,
-        );
-        command_encoder.set_vertex_bytes(
-            shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexViewportSize as u64,
-            mem::size_of::<shaders::vector_float2>() as u64,
-            [drawable_size.to_float2()].as_ptr() as *const c_void,
-        );
-
-        for (atlas_id, sprites) in sprites_by_atlas {
-            align_offset(offset);
-            let next_offset = *offset + sprites.len() * mem::size_of::<shaders::GPUISprite>();
-            assert!(
-                next_offset <= INSTANCE_BUFFER_SIZE,
-                "instance buffer exhausted"
-            );
-
-            let texture = self.sprite_cache.atlas_texture(atlas_id).unwrap();
-            command_encoder.set_vertex_buffer(
-                shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexSprites as u64,
-                Some(&self.instances),
-                *offset as u64,
-            );
-            command_encoder.set_vertex_bytes(
-                shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexAtlasSize as u64,
-                mem::size_of::<shaders::vector_float2>() as u64,
-                [vec2i(texture.width() as i32, texture.height() as i32).to_float2()].as_ptr()
-                    as *const c_void,
-            );
-
-            command_encoder.set_fragment_texture(
-                shaders::GPUISpriteFragmentInputIndex_GPUISpriteFragmentInputIndexAtlas as u64,
-                Some(texture),
-            );
-
-            unsafe {
-                let buffer_contents =
-                    (self.instances.contents() as *mut u8).add(*offset) as *mut shaders::GPUISprite;
-                std::ptr::copy_nonoverlapping(sprites.as_ptr(), buffer_contents, sprites.len());
-            }
-
-            command_encoder.draw_primitives_instanced(
-                metal::MTLPrimitiveType::Triangle,
-                0,
-                6,
-                sprites.len() as u64,
-            );
-            *offset = next_offset;
-        }
-    }
-
-    fn render_images(
-        &mut self,
-        images: &[Image],
-        image_glyphs: &[ImageGlyph],
-        scale_factor: f32,
-        offset: &mut usize,
-        drawable_size: Vector2F,
-        command_encoder: &metal::RenderCommandEncoderRef,
-    ) {
-        if images.is_empty() && image_glyphs.is_empty() {
-            return;
-        }
-
-        let mut images_by_atlas = HashMap::new();
-        for image in images {
-            let origin = image.bounds.origin() * scale_factor;
-            let target_size = image.bounds.size() * scale_factor;
-            let corner_radii = image.corner_radii * scale_factor;
-            let (alloc_id, atlas_bounds) = self.image_cache.render(&image.data);
-            images_by_atlas
-                .entry(alloc_id.atlas_id)
-                .or_insert_with(Vec::new)
-                .push(shaders::GPUIImage {
-                    origin: origin.to_float2(),
-                    target_size: target_size.to_float2(),
-                    source_size: atlas_bounds.size().to_float2(),
-                    atlas_origin: atlas_bounds.origin().to_float2(),
-                    border_top: image.border.top * scale_factor,
-                    border_right: image.border.right * scale_factor,
-                    border_bottom: image.border.bottom * scale_factor,
-                    border_left: image.border.left * scale_factor,
-                    border_color: image.border.color.to_uchar4(),
-                    corner_radius_top_left: corner_radii.top_left,
-                    corner_radius_top_right: corner_radii.top_right,
-                    corner_radius_bottom_right: corner_radii.bottom_right,
-                    corner_radius_bottom_left: corner_radii.bottom_left,
-                    grayscale: image.grayscale as u8,
-                });
-        }
-
-        for image_glyph in image_glyphs {
-            let origin = (image_glyph.origin * scale_factor).floor();
-            if let Some((alloc_id, atlas_bounds, glyph_origin)) =
-                self.image_cache.render_glyph(image_glyph)
-            {
-                images_by_atlas
-                    .entry(alloc_id.atlas_id)
-                    .or_insert_with(Vec::new)
-                    .push(shaders::GPUIImage {
-                        origin: (origin + glyph_origin.to_f32()).to_float2(),
-                        target_size: atlas_bounds.size().to_float2(),
-                        source_size: atlas_bounds.size().to_float2(),
-                        atlas_origin: atlas_bounds.origin().to_float2(),
-                        border_top: 0.,
-                        border_right: 0.,
-                        border_bottom: 0.,
-                        border_left: 0.,
-                        border_color: Default::default(),
-                        corner_radius_top_left: 0.,
-                        corner_radius_top_right: 0.,
-                        corner_radius_bottom_right: 0.,
-                        corner_radius_bottom_left: 0.,
-                        grayscale: false as u8,
-                    });
-            } else {
-                log::warn!("could not render glyph with id {}", image_glyph.id);
-            }
-        }
-
-        command_encoder.set_render_pipeline_state(&self.image_pipeline_state);
-        command_encoder.set_vertex_buffer(
-            shaders::GPUIImageVertexInputIndex_GPUIImageVertexInputIndexVertices as u64,
-            Some(&self.unit_vertices),
-            0,
-        );
-        command_encoder.set_vertex_bytes(
-            shaders::GPUIImageVertexInputIndex_GPUIImageVertexInputIndexViewportSize as u64,
-            mem::size_of::<shaders::vector_float2>() as u64,
-            [drawable_size.to_float2()].as_ptr() as *const c_void,
-        );
-
-        for (atlas_id, images) in images_by_atlas {
-            align_offset(offset);
-            let next_offset = *offset + images.len() * mem::size_of::<shaders::GPUIImage>();
-            assert!(
-                next_offset <= INSTANCE_BUFFER_SIZE,
-                "instance buffer exhausted"
-            );
-
-            let texture = self.image_cache.atlas_texture(atlas_id).unwrap();
-            command_encoder.set_vertex_buffer(
-                shaders::GPUIImageVertexInputIndex_GPUIImageVertexInputIndexImages as u64,
-                Some(&self.instances),
-                *offset as u64,
-            );
-            command_encoder.set_vertex_bytes(
-                shaders::GPUIImageVertexInputIndex_GPUIImageVertexInputIndexAtlasSize as u64,
-                mem::size_of::<shaders::vector_float2>() as u64,
-                [vec2i(texture.width() as i32, texture.height() as i32).to_float2()].as_ptr()
-                    as *const c_void,
-            );
-            command_encoder.set_fragment_texture(
-                shaders::GPUIImageFragmentInputIndex_GPUIImageFragmentInputIndexAtlas as u64,
-                Some(texture),
-            );
-
-            unsafe {
-                let buffer_contents =
-                    (self.instances.contents() as *mut u8).add(*offset) as *mut shaders::GPUIImage;
-                std::ptr::copy_nonoverlapping(images.as_ptr(), buffer_contents, images.len());
-            }
-
-            command_encoder.draw_primitives_instanced(
-                metal::MTLPrimitiveType::Triangle,
-                0,
-                6,
-                images.len() as u64,
-            );
-            *offset = next_offset;
-        }
-    }
-
-    fn render_surfaces(
-        &mut self,
-        surfaces: &[Surface],
-        scale_factor: f32,
-        offset: &mut usize,
-        drawable_size: Vector2F,
-        command_encoder: &metal::RenderCommandEncoderRef,
-    ) {
-        if surfaces.is_empty() {
-            return;
-        }
-
-        command_encoder.set_render_pipeline_state(&self.surface_pipeline_state);
-        command_encoder.set_vertex_buffer(
-            shaders::GPUISurfaceVertexInputIndex_GPUISurfaceVertexInputIndexVertices as u64,
-            Some(&self.unit_vertices),
-            0,
-        );
-        command_encoder.set_vertex_bytes(
-            shaders::GPUISurfaceVertexInputIndex_GPUISurfaceVertexInputIndexViewportSize as u64,
-            mem::size_of::<shaders::vector_float2>() as u64,
-            [drawable_size.to_float2()].as_ptr() as *const c_void,
-        );
-
-        for surface in surfaces {
-            let origin = surface.bounds.origin() * scale_factor;
-            let source_size = vec2i(
-                surface.image_buffer.width() as i32,
-                surface.image_buffer.height() as i32,
-            );
-            let target_size = surface.bounds.size() * scale_factor;
-
-            assert_eq!(
-                surface.image_buffer.pixel_format_type(),
-                core_video::kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
-            );
-
-            let y_texture = unsafe {
-                self.cv_texture_cache
-                    .create_texture_from_image(
-                        surface.image_buffer.as_concrete_TypeRef(),
-                        ptr::null(),
-                        MTLPixelFormat::R8Unorm,
-                        surface.image_buffer.plane_width(0),
-                        surface.image_buffer.plane_height(0),
-                        0,
-                    )
-                    .unwrap()
-            };
-            let cb_cr_texture = unsafe {
-                self.cv_texture_cache
-                    .create_texture_from_image(
-                        surface.image_buffer.as_concrete_TypeRef(),
-                        ptr::null(),
-                        MTLPixelFormat::RG8Unorm,
-                        surface.image_buffer.plane_width(1),
-                        surface.image_buffer.plane_height(1),
-                        1,
-                    )
-                    .unwrap()
-            };
-
-            align_offset(offset);
-            let next_offset = *offset + mem::size_of::<shaders::GPUISurface>();
-            assert!(
-                next_offset <= INSTANCE_BUFFER_SIZE,
-                "instance buffer exhausted"
-            );
-
-            command_encoder.set_vertex_buffer(
-                shaders::GPUISurfaceVertexInputIndex_GPUISurfaceVertexInputIndexSurfaces as u64,
-                Some(&self.instances),
-                *offset as u64,
-            );
-            command_encoder.set_vertex_bytes(
-                shaders::GPUISurfaceVertexInputIndex_GPUISurfaceVertexInputIndexAtlasSize as u64,
-                mem::size_of::<shaders::vector_float2>() as u64,
-                [source_size.to_float2()].as_ptr() as *const c_void,
-            );
-            command_encoder.set_fragment_texture(
-                shaders::GPUISurfaceFragmentInputIndex_GPUISurfaceFragmentInputIndexYAtlas as u64,
-                Some(y_texture.as_texture_ref()),
-            );
-            command_encoder.set_fragment_texture(
-                shaders::GPUISurfaceFragmentInputIndex_GPUISurfaceFragmentInputIndexCbCrAtlas
-                    as u64,
-                Some(cb_cr_texture.as_texture_ref()),
-            );
-
-            unsafe {
-                let buffer_contents = (self.instances.contents() as *mut u8).add(*offset)
-                    as *mut shaders::GPUISurface;
-                std::ptr::write(
-                    buffer_contents,
-                    shaders::GPUISurface {
-                        origin: origin.to_float2(),
-                        target_size: target_size.to_float2(),
-                        source_size: source_size.to_float2(),
-                    },
-                );
-            }
-
-            command_encoder.draw_primitives(metal::MTLPrimitiveType::Triangle, 0, 6);
-            *offset = next_offset;
-        }
-    }
-
-    fn render_path_sprites(
-        &mut self,
-        layer_id: usize,
-        sprites: &mut Peekable<vec::IntoIter<PathSprite>>,
-        offset: &mut usize,
-        drawable_size: Vector2F,
-        command_encoder: &metal::RenderCommandEncoderRef,
-    ) {
-        command_encoder.set_render_pipeline_state(&self.sprite_pipeline_state);
-        command_encoder.set_vertex_buffer(
-            shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexVertices as u64,
-            Some(&self.unit_vertices),
-            0,
-        );
-        command_encoder.set_vertex_bytes(
-            shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexViewportSize as u64,
-            mem::size_of::<shaders::vector_float2>() as u64,
-            [drawable_size.to_float2()].as_ptr() as *const c_void,
-        );
-
-        let mut atlas_id = None;
-        let mut atlas_sprite_count = 0;
-        align_offset(offset);
-
-        while let Some(sprite) = sprites.peek() {
-            if sprite.layer_id != layer_id {
-                break;
-            }
-
-            let sprite = sprites.next().unwrap();
-            if let Some(atlas_id) = atlas_id.as_mut() {
-                if sprite.atlas_id != *atlas_id {
-                    self.render_path_sprites_for_atlas(
-                        offset,
-                        *atlas_id,
-                        atlas_sprite_count,
-                        command_encoder,
-                    );
-
-                    *atlas_id = sprite.atlas_id;
-                    atlas_sprite_count = 0;
-                    align_offset(offset);
-                }
-            } else {
-                atlas_id = Some(sprite.atlas_id);
-            }
-
-            unsafe {
-                let buffer_contents =
-                    (self.instances.contents() as *mut u8).add(*offset) as *mut shaders::GPUISprite;
-                *buffer_contents.add(atlas_sprite_count) = sprite.shader_data;
-            }
-
-            atlas_sprite_count += 1;
-        }
-
-        if let Some(atlas_id) = atlas_id {
-            self.render_path_sprites_for_atlas(
-                offset,
-                atlas_id,
-                atlas_sprite_count,
-                command_encoder,
-            );
-        }
-    }
-
-    fn render_path_sprites_for_atlas(
-        &mut self,
-        offset: &mut usize,
-        atlas_id: usize,
-        sprite_count: usize,
-        command_encoder: &metal::RenderCommandEncoderRef,
-    ) {
-        let next_offset = *offset + sprite_count * mem::size_of::<shaders::GPUISprite>();
-        assert!(
-            next_offset <= INSTANCE_BUFFER_SIZE,
-            "instance buffer exhausted"
-        );
-        command_encoder.set_vertex_buffer(
-            shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexSprites as u64,
-            Some(&self.instances),
-            *offset as u64,
-        );
-        let texture = self.path_atlases.texture(atlas_id).unwrap();
-        command_encoder.set_fragment_texture(
-            shaders::GPUISpriteFragmentInputIndex_GPUISpriteFragmentInputIndexAtlas as u64,
-            Some(texture),
-        );
-        command_encoder.set_vertex_bytes(
-            shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexAtlasSize as u64,
-            mem::size_of::<shaders::vector_float2>() as u64,
-            [vec2i(texture.width() as i32, texture.height() as i32).to_float2()].as_ptr()
-                as *const c_void,
-        );
-
-        command_encoder.draw_primitives_instanced(
-            metal::MTLPrimitiveType::Triangle,
-            0,
-            6,
-            sprite_count as u64,
-        );
-        *offset = next_offset;
-    }
-
-    fn render_underlines(
-        &mut self,
-        underlines: &[Underline],
-        scale_factor: f32,
-        offset: &mut usize,
-        drawable_size: Vector2F,
-        command_encoder: &metal::RenderCommandEncoderRef,
-    ) {
-        if underlines.is_empty() {
-            return;
-        }
-        align_offset(offset);
-        let next_offset = *offset + underlines.len() * mem::size_of::<shaders::GPUIUnderline>();
-        assert!(
-            next_offset <= INSTANCE_BUFFER_SIZE,
-            "instance buffer exhausted"
-        );
-
-        command_encoder.set_render_pipeline_state(&self.underline_pipeline_state);
-        command_encoder.set_vertex_buffer(
-            shaders::GPUIUnderlineInputIndex_GPUIUnderlineInputIndexVertices as u64,
-            Some(&self.unit_vertices),
-            0,
-        );
-        command_encoder.set_vertex_buffer(
-            shaders::GPUIUnderlineInputIndex_GPUIUnderlineInputIndexUnderlines as u64,
-            Some(&self.instances),
-            *offset as u64,
-        );
-        command_encoder.set_vertex_bytes(
-            shaders::GPUIUnderlineInputIndex_GPUIUnderlineInputIndexUniforms as u64,
-            mem::size_of::<shaders::GPUIUniforms>() as u64,
-            [shaders::GPUIUniforms {
-                viewport_size: drawable_size.to_float2(),
-            }]
-            .as_ptr() as *const c_void,
-        );
-
-        let buffer_contents = unsafe {
-            (self.instances.contents() as *mut u8).add(*offset) as *mut shaders::GPUIUnderline
-        };
-        for (ix, underline) in underlines.iter().enumerate() {
-            let origin = underline.origin * scale_factor;
-            let mut height = underline.thickness;
-            if underline.squiggly {
-                height *= 3.;
-            }
-            let size = vec2f(underline.width, height) * scale_factor;
-            let shader_underline = shaders::GPUIUnderline {
-                origin: origin.round().to_float2(),
-                size: size.round().to_float2(),
-                thickness: underline.thickness * scale_factor,
-                color: underline.color.to_uchar4(),
-                squiggly: underline.squiggly as u8,
-            };
-            unsafe {
-                *(buffer_contents.add(ix)) = shader_underline;
-            }
-        }
-
-        command_encoder.draw_primitives_instanced(
-            metal::MTLPrimitiveType::Triangle,
-            0,
-            6,
-            underlines.len() as u64,
-        );
-        *offset = next_offset;
-    }
-}
-
-fn build_path_atlas_texture_descriptor() -> metal::TextureDescriptor {
-    let texture_descriptor = metal::TextureDescriptor::new();
-    texture_descriptor.set_width(2048);
-    texture_descriptor.set_height(2048);
-    texture_descriptor.set_pixel_format(MTLPixelFormat::R16Float);
-    texture_descriptor
-        .set_usage(metal::MTLTextureUsage::RenderTarget | metal::MTLTextureUsage::ShaderRead);
-    texture_descriptor.set_storage_mode(metal::MTLStorageMode::Private);
-    texture_descriptor
-}
-
-fn align_offset(offset: &mut usize) {
-    let r = *offset % 256;
-    if r > 0 {
-        *offset += 256 - r; // Align to a multiple of 256 to make Metal happy
-    }
-}
-
-fn build_pipeline_state(
-    device: &metal::DeviceRef,
-    library: &metal::LibraryRef,
-    label: &str,
-    vertex_fn_name: &str,
-    fragment_fn_name: &str,
-    pixel_format: metal::MTLPixelFormat,
-) -> metal::RenderPipelineState {
-    let vertex_fn = library
-        .get_function(vertex_fn_name, None)
-        .expect("error locating vertex function");
-    let fragment_fn = library
-        .get_function(fragment_fn_name, None)
-        .expect("error locating fragment function");
-
-    let descriptor = metal::RenderPipelineDescriptor::new();
-    descriptor.set_label(label);
-    descriptor.set_vertex_function(Some(vertex_fn.as_ref()));
-    descriptor.set_fragment_function(Some(fragment_fn.as_ref()));
-    let color_attachment = descriptor.color_attachments().object_at(0).unwrap();
-    color_attachment.set_pixel_format(pixel_format);
-    color_attachment.set_blending_enabled(true);
-    color_attachment.set_rgb_blend_operation(metal::MTLBlendOperation::Add);
-    color_attachment.set_alpha_blend_operation(metal::MTLBlendOperation::Add);
-    color_attachment.set_source_rgb_blend_factor(metal::MTLBlendFactor::SourceAlpha);
-    color_attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::One);
-    color_attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::OneMinusSourceAlpha);
-    color_attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::One);
-
-    device
-        .new_render_pipeline_state(&descriptor)
-        .expect("could not create render pipeline state")
-}
-
-fn build_path_atlas_pipeline_state(
-    device: &metal::DeviceRef,
-    library: &metal::LibraryRef,
-    label: &str,
-    vertex_fn_name: &str,
-    fragment_fn_name: &str,
-    pixel_format: metal::MTLPixelFormat,
-) -> metal::RenderPipelineState {
-    let vertex_fn = library
-        .get_function(vertex_fn_name, None)
-        .expect("error locating vertex function");
-    let fragment_fn = library
-        .get_function(fragment_fn_name, None)
-        .expect("error locating fragment function");
-
-    let descriptor = metal::RenderPipelineDescriptor::new();
-    descriptor.set_label(label);
-    descriptor.set_vertex_function(Some(vertex_fn.as_ref()));
-    descriptor.set_fragment_function(Some(fragment_fn.as_ref()));
-    let color_attachment = descriptor.color_attachments().object_at(0).unwrap();
-    color_attachment.set_pixel_format(pixel_format);
-    color_attachment.set_blending_enabled(true);
-    color_attachment.set_rgb_blend_operation(metal::MTLBlendOperation::Add);
-    color_attachment.set_alpha_blend_operation(metal::MTLBlendOperation::Add);
-    color_attachment.set_source_rgb_blend_factor(metal::MTLBlendFactor::One);
-    color_attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::One);
-    color_attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::One);
-    color_attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::One);
-
-    device
-        .new_render_pipeline_state(&descriptor)
-        .expect("could not create render pipeline state")
-}
-
-mod shaders {
-    #![allow(non_upper_case_globals)]
-    #![allow(non_camel_case_types)]
-    #![allow(non_snake_case)]
-
-    use crate::{
-        color::Color,
-        geometry::vector::{Vector2F, Vector2I},
-    };
-    use std::mem;
-
-    include!(concat!(env!("OUT_DIR"), "/shaders.rs"));
-
-    pub trait ToFloat2 {
-        fn to_float2(&self) -> vector_float2;
-    }
-
-    impl ToFloat2 for (f32, f32) {
-        fn to_float2(&self) -> vector_float2 {
-            unsafe {
-                let mut output = mem::transmute::<_, u32>(self.1.to_bits()) as vector_float2;
-                output <<= 32;
-                output |= mem::transmute::<_, u32>(self.0.to_bits()) as vector_float2;
-                output
-            }
-        }
-    }
-
-    impl ToFloat2 for Vector2F {
-        fn to_float2(&self) -> vector_float2 {
-            unsafe {
-                let mut output = mem::transmute::<_, u32>(self.y().to_bits()) as vector_float2;
-                output <<= 32;
-                output |= mem::transmute::<_, u32>(self.x().to_bits()) as vector_float2;
-                output
-            }
-        }
-    }
-
-    impl ToFloat2 for Vector2I {
-        fn to_float2(&self) -> vector_float2 {
-            self.to_f32().to_float2()
-        }
-    }
-
-    impl Color {
-        pub fn to_uchar4(&self) -> vector_uchar4 {
-            let mut vec = self.a as vector_uchar4;
-            vec <<= 8;
-            vec |= self.b as vector_uchar4;
-            vec <<= 8;
-            vec |= self.g as vector_uchar4;
-            vec <<= 8;
-            vec |= self.r as vector_uchar4;
-            vec
-        }
-    }
-}

crates/gpui/src/platform/mac/screen.rs 🔗

@@ -1,144 +0,0 @@
-use super::ns_string;
-use crate::platform;
-use cocoa::{
-    appkit::NSScreen,
-    base::{id, nil},
-    foundation::{NSArray, NSDictionary, NSPoint, NSRect, NSSize},
-};
-use core_foundation::{
-    number::{kCFNumberIntType, CFNumberGetValue, CFNumberRef},
-    uuid::{CFUUIDGetUUIDBytes, CFUUIDRef},
-};
-use core_graphics::display::CGDirectDisplayID;
-use pathfinder_geometry::{rect::RectF, vector::vec2f};
-use std::{any::Any, ffi::c_void};
-use uuid::Uuid;
-
-#[link(name = "ApplicationServices", kind = "framework")]
-extern "C" {
-    pub fn CGDisplayCreateUUIDFromDisplayID(display: CGDirectDisplayID) -> CFUUIDRef;
-}
-
-#[derive(Debug)]
-pub struct Screen {
-    pub(crate) native_screen: id,
-}
-
-impl Screen {
-    /// Get the screen with the given UUID.
-    pub fn find_by_id(uuid: Uuid) -> Option<Self> {
-        Self::all().find(|screen| platform::Screen::display_uuid(screen) == Some(uuid))
-    }
-
-    /// Get the primary screen - the one with the menu bar, and whose bottom left
-    /// corner is at the origin of the AppKit coordinate system.
-    fn primary() -> Self {
-        Self::all().next().unwrap()
-    }
-
-    pub fn all() -> impl Iterator<Item = Self> {
-        unsafe {
-            let native_screens = NSScreen::screens(nil);
-            (0..NSArray::count(native_screens)).map(move |ix| Screen {
-                native_screen: native_screens.objectAtIndex(ix),
-            })
-        }
-    }
-
-    /// Convert the given rectangle in screen coordinates from GPUI's
-    /// coordinate system to the AppKit coordinate system.
-    ///
-    /// In GPUI's coordinates, the origin is at the top left of the primary screen, with
-    /// the Y axis pointing downward. In the AppKit coordindate system, the origin is at the
-    /// bottom left of the primary screen, with the Y axis pointing upward.
-    pub(crate) fn screen_rect_to_native(rect: RectF) -> NSRect {
-        let primary_screen_height = unsafe { Self::primary().native_screen.frame().size.height };
-        NSRect::new(
-            NSPoint::new(
-                rect.origin_x() as f64,
-                primary_screen_height - rect.origin_y() as f64 - rect.height() as f64,
-            ),
-            NSSize::new(rect.width() as f64, rect.height() as f64),
-        )
-    }
-
-    /// Convert the given rectangle in screen coordinates from the AppKit
-    /// coordinate system to GPUI's coordinate system.
-    ///
-    /// In GPUI's coordinates, the origin is at the top left of the primary screen, with
-    /// the Y axis pointing downward. In the AppKit coordindate system, the origin is at the
-    /// bottom left of the primary screen, with the Y axis pointing upward.
-    pub(crate) fn screen_rect_from_native(rect: NSRect) -> RectF {
-        let primary_screen_height = unsafe { Self::primary().native_screen.frame().size.height };
-        RectF::new(
-            vec2f(
-                rect.origin.x as f32,
-                (primary_screen_height - rect.origin.y - rect.size.height) as f32,
-            ),
-            vec2f(rect.size.width as f32, rect.size.height as f32),
-        )
-    }
-}
-
-impl platform::Screen for Screen {
-    fn as_any(&self) -> &dyn Any {
-        self
-    }
-
-    fn display_uuid(&self) -> Option<uuid::Uuid> {
-        unsafe {
-            // Screen ids are not stable. Further, the default device id is also unstable across restarts.
-            // CGDisplayCreateUUIDFromDisplayID is stable but not exposed in the bindings we use.
-            // This approach is similar to that which winit takes
-            // https://github.com/rust-windowing/winit/blob/402cbd55f932e95dbfb4e8b5e8551c49e56ff9ac/src/platform_impl/macos/monitor.rs#L99
-            let device_description = self.native_screen.deviceDescription();
-
-            let key = ns_string("NSScreenNumber");
-            let device_id_obj = device_description.objectForKey_(key);
-            if device_id_obj.is_null() {
-                // Under some circumstances, especially display re-arrangements or display locking, we seem to get a null pointer
-                // to the device id. See: https://linear.app/zed-industries/issue/Z-257/lock-screen-crash-with-multiple-monitors
-                return None;
-            }
-
-            let mut device_id: u32 = 0;
-            CFNumberGetValue(
-                device_id_obj as CFNumberRef,
-                kCFNumberIntType,
-                (&mut device_id) as *mut _ as *mut c_void,
-            );
-            let cfuuid = CGDisplayCreateUUIDFromDisplayID(device_id as CGDirectDisplayID);
-            if cfuuid.is_null() {
-                return None;
-            }
-
-            let bytes = CFUUIDGetUUIDBytes(cfuuid);
-            Some(Uuid::from_bytes([
-                bytes.byte0,
-                bytes.byte1,
-                bytes.byte2,
-                bytes.byte3,
-                bytes.byte4,
-                bytes.byte5,
-                bytes.byte6,
-                bytes.byte7,
-                bytes.byte8,
-                bytes.byte9,
-                bytes.byte10,
-                bytes.byte11,
-                bytes.byte12,
-                bytes.byte13,
-                bytes.byte14,
-                bytes.byte15,
-            ]))
-        }
-    }
-
-    fn bounds(&self) -> RectF {
-        unsafe { Self::screen_rect_from_native(self.native_screen.frame()) }
-    }
-
-    fn content_bounds(&self) -> RectF {
-        unsafe { Self::screen_rect_from_native(self.native_screen.visibleFrame()) }
-    }
-}

crates/gpui/src/platform/mac/shaders/shaders.h 🔗

@@ -1,135 +0,0 @@
-#include <simd/simd.h>
-
-typedef struct {
-  vector_float2 viewport_size;
-} GPUIUniforms;
-
-typedef enum {
-  GPUIQuadInputIndexVertices = 0,
-  GPUIQuadInputIndexQuads = 1,
-  GPUIQuadInputIndexUniforms = 2,
-} GPUIQuadInputIndex;
-
-typedef struct {
-  vector_float2 origin;
-  vector_float2 size;
-  vector_uchar4 background_color;
-  float border_top;
-  float border_right;
-  float border_bottom;
-  float border_left;
-  vector_uchar4 border_color;
-  float corner_radius_top_left;
-  float corner_radius_top_right;
-  float corner_radius_bottom_right;
-  float corner_radius_bottom_left;
-} GPUIQuad;
-
-typedef enum {
-  GPUIShadowInputIndexVertices = 0,
-  GPUIShadowInputIndexShadows = 1,
-  GPUIShadowInputIndexUniforms = 2,
-} GPUIShadowInputIndex;
-
-typedef struct {
-  vector_float2 origin;
-  vector_float2 size;
-  float corner_radius_top_left;
-  float corner_radius_top_right;
-  float corner_radius_bottom_right;
-  float corner_radius_bottom_left;
-  float sigma;
-  vector_uchar4 color;
-} GPUIShadow;
-
-typedef enum {
-  GPUISpriteVertexInputIndexVertices = 0,
-  GPUISpriteVertexInputIndexSprites = 1,
-  GPUISpriteVertexInputIndexViewportSize = 2,
-  GPUISpriteVertexInputIndexAtlasSize = 3,
-} GPUISpriteVertexInputIndex;
-
-typedef enum {
-  GPUISpriteFragmentInputIndexAtlas = 0,
-} GPUISpriteFragmentInputIndex;
-
-typedef struct {
-  vector_float2 origin;
-  vector_float2 target_size;
-  vector_float2 source_size;
-  vector_float2 atlas_origin;
-  vector_uchar4 color;
-  uint8_t compute_winding;
-} GPUISprite;
-
-typedef enum {
-  GPUIPathAtlasVertexInputIndexVertices = 0,
-  GPUIPathAtlasVertexInputIndexAtlasSize = 1,
-} GPUIPathAtlasVertexInputIndex;
-
-typedef struct {
-  vector_float2 xy_position;
-  vector_float2 st_position;
-  vector_float2 clip_rect_origin;
-  vector_float2 clip_rect_size;
-} GPUIPathVertex;
-
-typedef enum {
-  GPUIImageVertexInputIndexVertices = 0,
-  GPUIImageVertexInputIndexImages = 1,
-  GPUIImageVertexInputIndexViewportSize = 2,
-  GPUIImageVertexInputIndexAtlasSize = 3,
-} GPUIImageVertexInputIndex;
-
-typedef enum {
-  GPUIImageFragmentInputIndexAtlas = 0,
-} GPUIImageFragmentInputIndex;
-
-typedef struct {
-  vector_float2 origin;
-  vector_float2 target_size;
-  vector_float2 source_size;
-  vector_float2 atlas_origin;
-  float border_top;
-  float border_right;
-  float border_bottom;
-  float border_left;
-  vector_uchar4 border_color;
-  float corner_radius_top_left;
-  float corner_radius_top_right;
-  float corner_radius_bottom_right;
-  float corner_radius_bottom_left;
-  uint8_t grayscale;
-} GPUIImage;
-
-typedef enum {
-  GPUISurfaceVertexInputIndexVertices = 0,
-  GPUISurfaceVertexInputIndexSurfaces = 1,
-  GPUISurfaceVertexInputIndexViewportSize = 2,
-  GPUISurfaceVertexInputIndexAtlasSize = 3,
-} GPUISurfaceVertexInputIndex;
-
-typedef enum {
-  GPUISurfaceFragmentInputIndexYAtlas = 0,
-  GPUISurfaceFragmentInputIndexCbCrAtlas = 1,
-} GPUISurfaceFragmentInputIndex;
-
-typedef struct {
-  vector_float2 origin;
-  vector_float2 target_size;
-  vector_float2 source_size;
-} GPUISurface;
-
-typedef enum {
-  GPUIUnderlineInputIndexVertices = 0,
-  GPUIUnderlineInputIndexUnderlines = 1,
-  GPUIUnderlineInputIndexUniforms = 2,
-} GPUIUnderlineInputIndex;
-
-typedef struct {
-  vector_float2 origin;
-  vector_float2 size;
-  float thickness;
-  vector_uchar4 color;
-  uint8_t squiggly;
-} GPUIUnderline;

crates/gpui/src/platform/mac/shaders/shaders.metal 🔗

@@ -1,464 +0,0 @@
-#include <metal_stdlib>
-#include "shaders.h"
-
-using namespace metal;
-
-float4 coloru_to_colorf(uchar4 coloru) {
-    return float4(coloru) / float4(0xff, 0xff, 0xff, 0xff);
-}
-
-float4 to_device_position(float2 pixel_position, float2 viewport_size) {
-    return float4(pixel_position / viewport_size * float2(2., -2.) + float2(-1., 1.), 0., 1.);
-}
-
-// A standard gaussian function, used for weighting samples
-float gaussian(float x, float sigma) {
-    return exp(-(x * x) / (2. * sigma * sigma)) / (sqrt(2. * M_PI_F) * sigma);
-}
-
-// This approximates the error function, needed for the gaussian integral
-float2 erf(float2 x) {
-    float2 s = sign(x);
-    float2 a = abs(x);
-    x = 1. + (0.278393 + (0.230389 + 0.078108 * (a * a)) * a) * a;
-    x *= x;
-    return s - s / (x * x);
-}
-
-float blur_along_x(float x, float y, float sigma, float corner, float2 halfSize) {
-    float delta = min(halfSize.y - corner - abs(y), 0.);
-    float curved = halfSize.x - corner + sqrt(max(0., corner * corner - delta * delta));
-    float2 integral = 0.5 + 0.5 * erf((x + float2(-curved, curved)) * (sqrt(0.5) / sigma));
-    return integral.y - integral.x;
-}
-
-struct QuadFragmentInput {
-    float4 position [[position]];
-    float2 atlas_position; // only used in the image shader
-    float2 origin;
-    float2 size;
-    float4 background_color;
-    float border_top;
-    float border_right;
-    float border_bottom;
-    float border_left;
-    float4 border_color;
-    float corner_radius_top_left;
-    float corner_radius_top_right;
-    float corner_radius_bottom_right;
-    float corner_radius_bottom_left;
-    uchar grayscale; // only used in image shader
-};
-
-float4 quad_sdf(QuadFragmentInput input) {
-    float2 half_size = input.size / 2.;
-    float2 center = input.origin + half_size;
-    float2 center_to_point = input.position.xy - center;
-    float corner_radius;
-    if (center_to_point.x < 0.) {
-        if (center_to_point.y < 0.) {
-            corner_radius = input.corner_radius_top_left;
-        } else {
-            corner_radius = input.corner_radius_bottom_left;
-        }
-    } else {
-        if (center_to_point.y < 0.) {
-            corner_radius = input.corner_radius_top_right;
-        } else {
-            corner_radius = input.corner_radius_bottom_right;
-        }
-    }
-
-    float2 rounded_edge_to_point = abs(center_to_point) - half_size + corner_radius;
-    float distance = length(max(0., rounded_edge_to_point)) + min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y)) - corner_radius;
-
-    float vertical_border = center_to_point.x <= 0. ? input.border_left : input.border_right;
-    float horizontal_border = center_to_point.y <= 0. ? input.border_top : input.border_bottom;
-    float2 inset_size = half_size - corner_radius - float2(vertical_border, horizontal_border);
-    float2 point_to_inset_corner = abs(center_to_point) - inset_size;
-    float border_width;
-    if (point_to_inset_corner.x < 0. && point_to_inset_corner.y < 0.) {
-        border_width = 0.;
-    } else if (point_to_inset_corner.y > point_to_inset_corner.x) {
-        border_width = horizontal_border;
-    } else {
-        border_width = vertical_border;
-    }
-
-    float4 color;
-    if (border_width == 0.) {
-        color = input.background_color;
-    } else {
-        float inset_distance = distance + border_width;
-
-        // Decrease border's opacity as we move inside the background.
-        input.border_color.a *= 1. - saturate(0.5 - inset_distance);
-
-        // Alpha-blend the border and the background.
-        float output_alpha = input.border_color.a + input.background_color.a * (1. - input.border_color.a);
-        float3 premultiplied_border_rgb = input.border_color.rgb * input.border_color.a;
-        float3 premultiplied_background_rgb = input.background_color.rgb * input.background_color.a;
-        float3 premultiplied_output_rgb = premultiplied_border_rgb + premultiplied_background_rgb * (1. - input.border_color.a);
-        color = float4(premultiplied_output_rgb / output_alpha, output_alpha);
-    }
-
-    return color * float4(1., 1., 1., saturate(0.5 - distance));
-}
-
-vertex QuadFragmentInput quad_vertex(
-    uint unit_vertex_id [[vertex_id]],
-    uint quad_id [[instance_id]],
-    constant float2 *unit_vertices [[buffer(GPUIQuadInputIndexVertices)]],
-    constant GPUIQuad *quads [[buffer(GPUIQuadInputIndexQuads)]],
-    constant GPUIUniforms *uniforms [[buffer(GPUIQuadInputIndexUniforms)]]
-) {
-    float2 unit_vertex = unit_vertices[unit_vertex_id];
-    GPUIQuad quad = quads[quad_id];
-    float2 position = unit_vertex * quad.size + quad.origin;
-    float4 device_position = to_device_position(position, uniforms->viewport_size);
-
-    return QuadFragmentInput {
-        device_position,
-        float2(0., 0.),
-        quad.origin,
-        quad.size,
-        coloru_to_colorf(quad.background_color),
-        quad.border_top,
-        quad.border_right,
-        quad.border_bottom,
-        quad.border_left,
-        coloru_to_colorf(quad.border_color),
-        quad.corner_radius_top_left,
-        quad.corner_radius_top_right,
-        quad.corner_radius_bottom_right,
-        quad.corner_radius_bottom_left,
-        0,
-    };
-}
-
-fragment float4 quad_fragment(
-    QuadFragmentInput input [[stage_in]]
-) {
-    return quad_sdf(input);
-}
-
-struct ShadowFragmentInput {
-    float4 position [[position]];
-    vector_float2 origin;
-    vector_float2 size;
-    float corner_radius_top_left;
-    float corner_radius_top_right;
-    float corner_radius_bottom_right;
-    float corner_radius_bottom_left;
-    float sigma;
-    vector_uchar4 color;
-};
-
-vertex ShadowFragmentInput shadow_vertex(
-    uint unit_vertex_id [[vertex_id]],
-    uint shadow_id [[instance_id]],
-    constant float2 *unit_vertices [[buffer(GPUIShadowInputIndexVertices)]],
-    constant GPUIShadow *shadows [[buffer(GPUIShadowInputIndexShadows)]],
-    constant GPUIUniforms *uniforms [[buffer(GPUIShadowInputIndexUniforms)]]
-) {
-    float2 unit_vertex = unit_vertices[unit_vertex_id];
-    GPUIShadow shadow = shadows[shadow_id];
-
-    float margin = 3. * shadow.sigma;
-    float2 position = unit_vertex * (shadow.size + 2. * margin) + shadow.origin - margin;
-    float4 device_position = to_device_position(position, uniforms->viewport_size);
-
-    return ShadowFragmentInput {
-        device_position,
-        shadow.origin,
-        shadow.size,
-        shadow.corner_radius_top_left,
-        shadow.corner_radius_top_right,
-        shadow.corner_radius_bottom_right,
-        shadow.corner_radius_bottom_left,
-        shadow.sigma,
-        shadow.color,
-    };
-}
-
-fragment float4 shadow_fragment(
-    ShadowFragmentInput input [[stage_in]]
-) {
-    float sigma = input.sigma;
-    float2 half_size = input.size / 2.;
-    float2 center = input.origin + half_size;
-    float2 point = input.position.xy - center;
-    float2 center_to_point = input.position.xy - center;
-    float corner_radius;
-    if (center_to_point.x < 0.) {
-        if (center_to_point.y < 0.) {
-            corner_radius = input.corner_radius_top_left;
-        } else {
-            corner_radius = input.corner_radius_bottom_left;
-        }
-    } else {
-        if (center_to_point.y < 0.) {
-            corner_radius = input.corner_radius_top_right;
-        } else {
-            corner_radius = input.corner_radius_bottom_right;
-        }
-    }
-
-    // The signal is only non-zero in a limited range, so don't waste samples
-    float low = point.y - half_size.y;
-    float high = point.y + half_size.y;
-    float start = clamp(-3. * sigma, low, high);
-    float end = clamp(3. * sigma, low, high);
-
-    // Accumulate samples (we can get away with surprisingly few samples)
-    float step = (end - start) / 4.;
-    float y = start + step * 0.5;
-    float alpha = 0.;
-    for (int i = 0; i < 4; i++) {
-        alpha += blur_along_x(point.x, point.y - y, sigma, corner_radius, half_size) * gaussian(y, sigma) * step;
-        y += step;
-    }
-
-    return float4(1., 1., 1., alpha) * coloru_to_colorf(input.color);
-}
-
-struct SpriteFragmentInput {
-    float4 position [[position]];
-    float2 atlas_position;
-    float4 color [[flat]];
-    uchar compute_winding [[flat]];
-};
-
-vertex SpriteFragmentInput sprite_vertex(
-    uint unit_vertex_id [[vertex_id]],
-    uint sprite_id [[instance_id]],
-    constant float2 *unit_vertices [[buffer(GPUISpriteVertexInputIndexVertices)]],
-    constant GPUISprite *sprites [[buffer(GPUISpriteVertexInputIndexSprites)]],
-    constant float2 *viewport_size [[buffer(GPUISpriteVertexInputIndexViewportSize)]],
-    constant float2 *atlas_size [[buffer(GPUISpriteVertexInputIndexAtlasSize)]]
-) {
-    float2 unit_vertex = unit_vertices[unit_vertex_id];
-    GPUISprite sprite = sprites[sprite_id];
-    float2 position = unit_vertex * sprite.target_size + sprite.origin;
-    float4 device_position = to_device_position(position, *viewport_size);
-    float2 atlas_position = (unit_vertex * sprite.source_size + sprite.atlas_origin) / *atlas_size;
-
-    return SpriteFragmentInput {
-        device_position,
-        atlas_position,
-        coloru_to_colorf(sprite.color),
-        sprite.compute_winding
-    };
-}
-
-fragment float4 sprite_fragment(
-    SpriteFragmentInput input [[stage_in]],
-    texture2d<float> atlas [[ texture(GPUISpriteFragmentInputIndexAtlas) ]]
-) {
-    constexpr sampler atlas_sampler(mag_filter::linear, min_filter::linear);
-    float4 color = input.color;
-    float4 sample = atlas.sample(atlas_sampler, input.atlas_position);
-    float mask;
-    if (input.compute_winding) {
-        mask = 1. - abs(1. - fmod(sample.r, 2.));
-    } else {
-        mask = sample.a;
-    }
-    color.a *= mask;
-    return color;
-}
-
-vertex QuadFragmentInput image_vertex(
-    uint unit_vertex_id [[vertex_id]],
-    uint image_id [[instance_id]],
-    constant float2 *unit_vertices [[buffer(GPUIImageVertexInputIndexVertices)]],
-    constant GPUIImage *images [[buffer(GPUIImageVertexInputIndexImages)]],
-    constant float2 *viewport_size [[buffer(GPUIImageVertexInputIndexViewportSize)]],
-    constant float2 *atlas_size [[buffer(GPUIImageVertexInputIndexAtlasSize)]]
-) {
-    float2 unit_vertex = unit_vertices[unit_vertex_id];
-    GPUIImage image = images[image_id];
-    float2 position = unit_vertex * image.target_size + image.origin;
-    float4 device_position = to_device_position(position, *viewport_size);
-    float2 atlas_position = (unit_vertex * image.source_size + image.atlas_origin) / *atlas_size;
-
-    return QuadFragmentInput {
-        device_position,
-        atlas_position,
-        image.origin,
-        image.target_size,
-        float4(0.),
-        image.border_top,
-        image.border_right,
-        image.border_bottom,
-        image.border_left,
-        coloru_to_colorf(image.border_color),
-        image.corner_radius_top_left,
-        image.corner_radius_top_right,
-        image.corner_radius_bottom_right,
-        image.corner_radius_bottom_left,
-        image.grayscale,
-    };
-}
-
-fragment float4 image_fragment(
-    QuadFragmentInput input [[stage_in]],
-    texture2d<float> atlas [[ texture(GPUIImageFragmentInputIndexAtlas) ]]
-) {
-    constexpr sampler atlas_sampler(mag_filter::linear, min_filter::linear);
-    input.background_color = atlas.sample(atlas_sampler, input.atlas_position);
-    if (input.grayscale) {
-        float grayscale =
-            0.2126 * input.background_color.r +
-            0.7152 * input.background_color.g +
-            0.0722 * input.background_color.b;
-        input.background_color = float4(grayscale, grayscale, grayscale, input.background_color.a);
-    }
-    return quad_sdf(input);
-}
-
-vertex QuadFragmentInput surface_vertex(
-    uint unit_vertex_id [[vertex_id]],
-    uint image_id [[instance_id]],
-    constant float2 *unit_vertices [[buffer(GPUISurfaceVertexInputIndexVertices)]],
-    constant GPUISurface *images [[buffer(GPUISurfaceVertexInputIndexSurfaces)]],
-    constant float2 *viewport_size [[buffer(GPUISurfaceVertexInputIndexViewportSize)]],
-    constant float2 *atlas_size [[buffer(GPUISurfaceVertexInputIndexAtlasSize)]]
-) {
-    float2 unit_vertex = unit_vertices[unit_vertex_id];
-    GPUISurface image = images[image_id];
-    float2 position = unit_vertex * image.target_size + image.origin;
-    float4 device_position = to_device_position(position, *viewport_size);
-    float2 atlas_position = (unit_vertex * image.source_size) / *atlas_size;
-
-    return QuadFragmentInput {
-        device_position,
-        atlas_position,
-        image.origin,
-        image.target_size,
-        float4(0.),
-        0.,
-        0.,
-        0.,
-        0.,
-        float4(0.),
-        0.,
-        0,
-    };
-}
-
-fragment float4 surface_fragment(
-    QuadFragmentInput input [[stage_in]],
-    texture2d<float> y_atlas [[ texture(GPUISurfaceFragmentInputIndexYAtlas) ]],
-    texture2d<float> cb_cr_atlas [[ texture(GPUISurfaceFragmentInputIndexCbCrAtlas) ]]
-) {
-    constexpr sampler atlas_sampler(mag_filter::linear, min_filter::linear);
-    const float4x4 ycbcrToRGBTransform = float4x4(
-        float4(+1.0000f, +1.0000f, +1.0000f, +0.0000f),
-        float4(+0.0000f, -0.3441f, +1.7720f, +0.0000f),
-        float4(+1.4020f, -0.7141f, +0.0000f, +0.0000f),
-        float4(-0.7010f, +0.5291f, -0.8860f, +1.0000f)
-    );
-    float4 ycbcr = float4(y_atlas.sample(atlas_sampler, input.atlas_position).r,
-                          cb_cr_atlas.sample(atlas_sampler, input.atlas_position).rg, 1.0);
-
-    input.background_color = ycbcrToRGBTransform * ycbcr;
-    return quad_sdf(input);
-}
-
-struct PathAtlasVertexOutput {
-    float4 position [[position]];
-    float2 st_position;
-    float clip_rect_distance [[clip_distance]] [4];
-};
-
-struct PathAtlasFragmentInput {
-    float4 position [[position]];
-    float2 st_position;
-};
-
-vertex PathAtlasVertexOutput path_atlas_vertex(
-    uint vertex_id [[vertex_id]],
-    constant GPUIPathVertex *vertices [[buffer(GPUIPathAtlasVertexInputIndexVertices)]],
-    constant float2 *atlas_size [[buffer(GPUIPathAtlasVertexInputIndexAtlasSize)]]
-) {
-    GPUIPathVertex v = vertices[vertex_id];
-    float4 device_position = to_device_position(v.xy_position, *atlas_size);
-    return PathAtlasVertexOutput {
-        device_position,
-        v.st_position,
-        {
-            v.xy_position.x - v.clip_rect_origin.x,
-            v.clip_rect_origin.x + v.clip_rect_size.x - v.xy_position.x,
-            v.xy_position.y - v.clip_rect_origin.y,
-            v.clip_rect_origin.y + v.clip_rect_size.y - v.xy_position.y
-        }
-    };
-}
-
-fragment float4 path_atlas_fragment(
-    PathAtlasFragmentInput input [[stage_in]]
-) {
-    float2 dx = dfdx(input.st_position);
-    float2 dy = dfdy(input.st_position);
-    float2 gradient = float2(
-        (2. * input.st_position.x) * dx.x - dx.y,
-        (2. * input.st_position.x) * dy.x - dy.y
-    );
-    float f = (input.st_position.x * input.st_position.x) - input.st_position.y;
-    float distance = f / length(gradient);
-    float alpha = saturate(0.5 - distance);
-    return float4(alpha, 0., 0., 1.);
-}
-
-struct UnderlineFragmentInput {
-    float4 position [[position]];
-    float2 origin;
-    float2 size;
-    float thickness;
-    float4 color;
-    bool squiggly;
-};
-
-vertex UnderlineFragmentInput underline_vertex(
-    uint unit_vertex_id [[vertex_id]],
-    uint underline_id [[instance_id]],
-    constant float2 *unit_vertices [[buffer(GPUIUnderlineInputIndexVertices)]],
-    constant GPUIUnderline *underlines [[buffer(GPUIUnderlineInputIndexUnderlines)]],
-    constant GPUIUniforms *uniforms [[buffer(GPUIUnderlineInputIndexUniforms)]]
-) {
-    float2 unit_vertex = unit_vertices[unit_vertex_id];
-    GPUIUnderline underline = underlines[underline_id];
-    float2 position = unit_vertex * underline.size + underline.origin;
-    float4 device_position = to_device_position(position, uniforms->viewport_size);
-
-    return UnderlineFragmentInput {
-        device_position,
-        underline.origin,
-        underline.size,
-        underline.thickness,
-        coloru_to_colorf(underline.color),
-        underline.squiggly != 0,
-    };
-}
-
-fragment float4 underline_fragment(
-    UnderlineFragmentInput input [[stage_in]]
-) {
-    if (input.squiggly) {
-        float half_thickness = input.thickness * 0.5;
-        float2 st = ((input.position.xy - input.origin) / input.size.y) - float2(0., 0.5);
-        float frequency = (M_PI_F * (3. * input.thickness)) / 8.;
-        float amplitude = 1. / (2. * input.thickness);
-        float sine = sin(st.x * frequency) * amplitude;
-        float dSine = cos(st.x * frequency) * amplitude * frequency;
-        float distance = (st.y - sine) / sqrt(1. + dSine * dSine);
-        float distance_in_pixels = distance * input.size.y;
-        float distance_from_top_border = distance_in_pixels - half_thickness;
-        float distance_from_bottom_border = distance_in_pixels + half_thickness;
-        float alpha = saturate(0.5 - max(-distance_from_bottom_border, distance_from_top_border));
-        return input.color * float4(1., 1., 1., alpha);
-    } else {
-        return input.color;
-    }
-}

crates/gpui/src/platform/mac/sprite_cache.rs 🔗

@@ -1,164 +0,0 @@
-use super::atlas::AtlasAllocator;
-use crate::{
-    fonts::{FontId, GlyphId},
-    geometry::vector::{vec2f, Vector2F, Vector2I},
-    platform::{self, RasterizationOptions},
-};
-use collections::hash_map::Entry;
-use metal::{MTLPixelFormat, TextureDescriptor};
-use ordered_float::OrderedFloat;
-use std::{borrow::Cow, collections::HashMap, sync::Arc};
-
-#[derive(Hash, Eq, PartialEq)]
-struct GlyphDescriptor {
-    font_id: FontId,
-    font_size: OrderedFloat<f32>,
-    glyph_id: GlyphId,
-    subpixel_variant: (u8, u8),
-}
-
-#[derive(Clone)]
-pub struct GlyphSprite {
-    pub atlas_id: usize,
-    pub atlas_origin: Vector2I,
-    pub offset: Vector2I,
-    pub size: Vector2I,
-}
-
-#[derive(Hash, Eq, PartialEq)]
-struct IconDescriptor {
-    path: Cow<'static, str>,
-    width: i32,
-    height: i32,
-}
-
-#[derive(Clone)]
-pub struct IconSprite {
-    pub atlas_id: usize,
-    pub atlas_origin: Vector2I,
-    pub size: Vector2I,
-}
-
-pub struct SpriteCache {
-    fonts: Arc<dyn platform::FontSystem>,
-    atlases: AtlasAllocator,
-    glyphs: HashMap<GlyphDescriptor, Option<GlyphSprite>>,
-    icons: HashMap<IconDescriptor, IconSprite>,
-    scale_factor: f32,
-}
-
-impl SpriteCache {
-    pub fn new(
-        device: metal::Device,
-        size: Vector2I,
-        scale_factor: f32,
-        fonts: Arc<dyn platform::FontSystem>,
-    ) -> Self {
-        let descriptor = TextureDescriptor::new();
-        descriptor.set_pixel_format(MTLPixelFormat::A8Unorm);
-        descriptor.set_width(size.x() as u64);
-        descriptor.set_height(size.y() as u64);
-        Self {
-            fonts,
-            atlases: AtlasAllocator::new(device, descriptor),
-            glyphs: Default::default(),
-            icons: Default::default(),
-            scale_factor,
-        }
-    }
-
-    pub fn set_scale_factor(&mut self, scale_factor: f32) {
-        if scale_factor != self.scale_factor {
-            self.icons.clear();
-            self.glyphs.clear();
-            self.atlases.clear();
-        }
-        self.scale_factor = scale_factor;
-    }
-
-    pub fn render_glyph(
-        &mut self,
-        font_id: FontId,
-        font_size: f32,
-        glyph_id: GlyphId,
-        target_position: Vector2F,
-    ) -> Option<GlyphSprite> {
-        const SUBPIXEL_VARIANTS: u8 = 4;
-
-        let target_position = target_position * self.scale_factor;
-        let subpixel_variant = (
-            (target_position.x().fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
-            (target_position.y().fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
-        );
-
-        self.glyphs
-            .entry(GlyphDescriptor {
-                font_id,
-                font_size: OrderedFloat(font_size),
-                glyph_id,
-                subpixel_variant,
-            })
-            .or_insert_with(|| {
-                let subpixel_shift = vec2f(
-                    subpixel_variant.0 as f32 / SUBPIXEL_VARIANTS as f32,
-                    subpixel_variant.1 as f32 / SUBPIXEL_VARIANTS as f32,
-                );
-                let (glyph_bounds, mask) = self.fonts.rasterize_glyph(
-                    font_id,
-                    font_size,
-                    glyph_id,
-                    subpixel_shift,
-                    self.scale_factor,
-                    RasterizationOptions::Alpha,
-                )?;
-
-                let (alloc_id, atlas_bounds) = self
-                    .atlases
-                    .upload(glyph_bounds.size(), &mask)
-                    .expect("could not upload glyph");
-                Some(GlyphSprite {
-                    atlas_id: alloc_id.atlas_id,
-                    atlas_origin: atlas_bounds.origin(),
-                    offset: glyph_bounds.origin(),
-                    size: glyph_bounds.size(),
-                })
-            })
-            .clone()
-    }
-
-    pub fn render_icon(
-        &mut self,
-        size: Vector2I,
-        path: Cow<'static, str>,
-        svg: usvg::Tree,
-    ) -> Option<IconSprite> {
-        let atlases = &mut self.atlases;
-        match self.icons.entry(IconDescriptor {
-            path,
-            width: size.x(),
-            height: size.y(),
-        }) {
-            Entry::Occupied(entry) => Some(entry.get().clone()),
-            Entry::Vacant(entry) => {
-                let mut pixmap = tiny_skia::Pixmap::new(size.x() as u32, size.y() as u32)?;
-                resvg::render(&svg, usvg::FitTo::Width(size.x() as u32), pixmap.as_mut());
-                let mask = pixmap
-                    .pixels()
-                    .iter()
-                    .map(|a| a.alpha())
-                    .collect::<Vec<_>>();
-                let (alloc_id, atlas_bounds) = atlases.upload(size, &mask)?;
-                let icon_sprite = IconSprite {
-                    atlas_id: alloc_id.atlas_id,
-                    atlas_origin: atlas_bounds.origin(),
-                    size,
-                };
-                Some(entry.insert(icon_sprite).clone())
-            }
-        }
-    }
-
-    pub fn atlas_texture(&self, atlas_id: usize) -> Option<&metal::TextureRef> {
-        self.atlases.texture(atlas_id)
-    }
-}

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

@@ -1,33 +1,29 @@
+use super::{display_bounds_from_native, ns_string, MacDisplay, MetalRenderer, NSRange};
 use crate::{
-    executor,
-    geometry::{
-        rect::RectF,
-        vector::{vec2f, Vector2F},
-    },
-    keymap_matcher::Keystroke,
-    platform::{
-        self,
-        mac::{
-            platform::NSViewLayerContentsRedrawDuringViewResize, renderer::Renderer, screen::Screen,
-        },
-        Event, InputHandler, KeyDownEvent, Modifiers, ModifiersChangedEvent, MouseButton,
-        MouseButtonEvent, MouseMovedEvent, Scene, WindowBounds, WindowKind,
-    },
-    AnyWindowHandle,
+    display_bounds_to_native, point, px, size, AnyWindowHandle, Bounds, DrawWindow, ExternalPaths,
+    FileDropEvent, ForegroundExecutor, GlobalPixels, InputEvent, KeyDownEvent, Keystroke,
+    Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
+    Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point,
+    PromptLevel, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions,
 };
 use block::ConcreteBlock;
 use cocoa::{
     appkit::{
-        CGPoint, NSApplication, NSBackingStoreBuffered, NSScreen, NSView, NSViewHeightSizable,
+        CGPoint, NSApplication, NSBackingStoreBuffered, NSEventModifierFlags,
+        NSFilenamesPboardType, NSPasteboard, NSScreen, NSView, NSViewHeightSizable,
         NSViewWidthSizable, NSWindow, NSWindowButton, NSWindowCollectionBehavior,
         NSWindowStyleMask, NSWindowTitleVisibility,
     },
     base::{id, nil},
-    foundation::{NSAutoreleasePool, NSInteger, NSPoint, NSRect, NSSize, NSString, NSUInteger},
+    foundation::{
+        NSArray, NSAutoreleasePool, NSDictionary, NSFastEnumeration, NSInteger, NSPoint, NSRect,
+        NSSize, NSString, NSUInteger,
+    },
 };
 use core_graphics::display::CGRect;
 use ctor::ctor;
 use foreign_types::ForeignTypeRef;
+use futures::channel::oneshot;
 use objc::{
     class,
     declare::ClassDecl,
@@ -35,26 +31,22 @@ use objc::{
     runtime::{Class, Object, Protocol, Sel, BOOL, NO, YES},
     sel, sel_impl,
 };
-use postage::oneshot;
-use smol::Timer;
+use parking_lot::Mutex;
+use smallvec::SmallVec;
 use std::{
     any::Any,
     cell::{Cell, RefCell},
-    convert::TryInto,
     ffi::{c_void, CStr},
     mem,
     ops::Range,
     os::raw::c_char,
+    path::PathBuf,
     ptr,
-    rc::{Rc, Weak},
-    sync::Arc,
+    rc::Rc,
+    sync::{Arc, Weak},
     time::Duration,
 };
-
-use super::{
-    geometry::{NSRectExt, Vector2FExt},
-    ns_string, NSRange,
-};
+use util::ResultExt;
 
 const WINDOW_STATE_IVAR: &str = "windowState";
 
@@ -79,10 +71,17 @@ const NSTrackingActiveAlways: NSUInteger = 0x80;
 const NSTrackingInVisibleRect: NSUInteger = 0x200;
 #[allow(non_upper_case_globals)]
 const NSWindowAnimationBehaviorUtilityWindow: NSInteger = 4;
+#[allow(non_upper_case_globals)]
+const NSViewLayerContentsRedrawDuringViewResize: NSInteger = 2;
+// https://developer.apple.com/documentation/appkit/nsdragoperation
+type NSDragOperation = NSUInteger;
+#[allow(non_upper_case_globals)]
+const NSDragOperationNone: NSDragOperation = 0;
+#[allow(non_upper_case_globals)]
+const NSDragOperationCopy: NSDragOperation = 1;
 
 #[ctor]
 unsafe fn build_classes() {
-    ::util::gpui1_loaded();
     WINDOW_CLASS = build_window_class("GPUIWindow", class!(NSWindow));
     PANEL_CLASS = build_window_class("GPUIPanel", class!(NSPanel));
     VIEW_CLASS = {
@@ -222,11 +221,11 @@ unsafe fn build_classes() {
     };
 }
 
-pub fn convert_mouse_position(position: NSPoint, window_height: f32) -> Vector2F {
-    vec2f(
-        position.x as f32,
+pub fn convert_mouse_position(position: NSPoint, window_height: Pixels) -> Point<Pixels> {
+    point(
+        px(position.x as f32),
         // MacOS screen coordinates are relative to bottom left
-        window_height - position.y as f32,
+        window_height - px(position.y as f32),
     )
 }
 
@@ -271,6 +270,28 @@ unsafe fn build_window_class(name: &'static str, superclass: &Class) -> *const C
         window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL,
     );
     decl.add_method(sel!(close), close_window as extern "C" fn(&Object, Sel));
+
+    decl.add_method(
+        sel!(draggingEntered:),
+        dragging_entered as extern "C" fn(&Object, Sel, id) -> NSDragOperation,
+    );
+    decl.add_method(
+        sel!(draggingUpdated:),
+        dragging_updated as extern "C" fn(&Object, Sel, id) -> NSDragOperation,
+    );
+    decl.add_method(
+        sel!(draggingExited:),
+        dragging_exited as extern "C" fn(&Object, Sel, id),
+    );
+    decl.add_method(
+        sel!(performDragOperation:),
+        perform_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL,
+    );
+    decl.add_method(
+        sel!(concludeDragOperation:),
+        conclude_drag_operation as extern "C" fn(&Object, Sel, id),
+    );
+
     decl.register()
 }
 
@@ -286,41 +307,40 @@ enum ImeState {
     None,
 }
 
-#[derive(Debug)]
 struct InsertText {
     replacement_range: Option<Range<usize>>,
     text: String,
 }
 
-struct WindowState {
+struct MacWindowState {
     handle: AnyWindowHandle,
+    executor: ForegroundExecutor,
     native_window: id,
+    renderer: MetalRenderer,
+    draw: Option<DrawWindow>,
     kind: WindowKind,
-    event_callback: Option<Box<dyn FnMut(Event) -> bool>>,
+    event_callback: Option<Box<dyn FnMut(InputEvent) -> bool>>,
     activate_callback: Option<Box<dyn FnMut(bool)>>,
-    resize_callback: Option<Box<dyn FnMut()>>,
+    resize_callback: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
     fullscreen_callback: Option<Box<dyn FnMut(bool)>>,
     moved_callback: Option<Box<dyn FnMut()>>,
     should_close_callback: Option<Box<dyn FnMut() -> bool>>,
     close_callback: Option<Box<dyn FnOnce()>>,
     appearance_changed_callback: Option<Box<dyn FnMut()>>,
-    input_handler: Option<Box<dyn InputHandler>>,
+    input_handler: Option<Box<dyn PlatformInputHandler>>,
     pending_key_down: Option<(KeyDownEvent, Option<InsertText>)>,
     last_key_equivalent: Option<KeyDownEvent>,
     synthetic_drag_counter: usize,
-    executor: Rc<executor::Foreground>,
-    scene_to_render: Option<Scene>,
-    renderer: Renderer,
     last_fresh_keydown: Option<Keystroke>,
-    traffic_light_position: Option<Vector2F>,
-    previous_modifiers_changed_event: Option<Event>,
-    //State tracking what the IME did after the last request
+    traffic_light_position: Option<Point<Pixels>>,
+    previous_modifiers_changed_event: Option<InputEvent>,
+    // State tracking what the IME did after the last request
     ime_state: ImeState,
-    //Retains the last IME Text
+    // Retains the last IME Text
     ime_text: Option<String>,
 }
 
-impl WindowState {
+impl MacWindowState {
     fn move_traffic_light(&self) {
         if let Some(traffic_light_position) = self.traffic_light_position {
             let titlebar_height = self.titlebar_height();
@@ -342,25 +362,26 @@ impl WindowState {
                 let mut close_button_frame: CGRect = msg_send![close_button, frame];
                 let mut min_button_frame: CGRect = msg_send![min_button, frame];
                 let mut zoom_button_frame: CGRect = msg_send![zoom_button, frame];
-                let mut origin = vec2f(
-                    traffic_light_position.x(),
+                let mut origin = point(
+                    traffic_light_position.x,
                     titlebar_height
-                        - traffic_light_position.y()
-                        - close_button_frame.size.height as f32,
+                        - traffic_light_position.y
+                        - px(close_button_frame.size.height as f32),
                 );
                 let button_spacing =
-                    (min_button_frame.origin.x - close_button_frame.origin.x) as f32;
+                    px((min_button_frame.origin.x - close_button_frame.origin.x) as f32);
 
-                close_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
+                close_button_frame.origin = CGPoint::new(origin.x.into(), origin.y.into());
                 let _: () = msg_send![close_button, setFrame: close_button_frame];
-                origin.set_x(origin.x() + button_spacing);
+                origin.x += button_spacing;
 
-                min_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
+                min_button_frame.origin = CGPoint::new(origin.x.into(), origin.y.into());
                 let _: () = msg_send![min_button, setFrame: min_button_frame];
-                origin.set_x(origin.x() + button_spacing);
+                origin.x += button_spacing;
 
-                zoom_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
+                zoom_button_frame.origin = CGPoint::new(origin.x.into(), origin.y.into());
                 let _: () = msg_send![zoom_button, setFrame: zoom_button_frame];
+                origin.x += button_spacing;
             }
         }
     }
@@ -379,8 +400,8 @@ impl WindowState {
             }
 
             let frame = self.frame();
-            let screen_size = self.native_window.screen().visibleFrame().size_vec();
-            if frame.size() == screen_size {
+            let screen_size = self.native_window.screen().visibleFrame().into();
+            if frame.size == screen_size {
                 WindowBounds::Maximized
             } else {
                 WindowBounds::Fixed(frame)
@@ -388,47 +409,52 @@ impl WindowState {
         }
     }
 
-    fn frame(&self) -> RectF {
+    fn frame(&self) -> Bounds<GlobalPixels> {
         unsafe {
             let frame = NSWindow::frame(self.native_window);
-            Screen::screen_rect_from_native(frame)
+            display_bounds_from_native(mem::transmute::<NSRect, CGRect>(frame))
         }
     }
 
-    fn content_size(&self) -> Vector2F {
+    fn content_size(&self) -> Size<Pixels> {
         let NSSize { width, height, .. } =
             unsafe { NSView::frame(self.native_window.contentView()) }.size;
-        vec2f(width as f32, height as f32)
+        size(px(width as f32), px(height as f32))
     }
 
     fn scale_factor(&self) -> f32 {
         get_scale_factor(self.native_window)
     }
 
-    fn titlebar_height(&self) -> f32 {
+    fn titlebar_height(&self) -> Pixels {
         unsafe {
             let frame = NSWindow::frame(self.native_window);
             let content_layout_rect: CGRect = msg_send![self.native_window, contentLayoutRect];
-            (frame.size.height - content_layout_rect.size.height) as f32
+            px((frame.size.height - content_layout_rect.size.height) as f32)
         }
     }
 
-    fn present_scene(&mut self, scene: Scene) {
-        self.scene_to_render = Some(scene);
+    fn to_screen_ns_point(&self, point: Point<Pixels>) -> NSPoint {
         unsafe {
-            let _: () = msg_send![self.native_window.contentView(), setNeedsDisplay: YES];
+            let point = NSPoint::new(
+                point.x.into(),
+                (self.content_size().height - point.y).into(),
+            );
+            msg_send![self.native_window, convertPointToScreen: point]
         }
     }
 }
 
-pub struct MacWindow(Rc<RefCell<WindowState>>);
+unsafe impl Send for MacWindowState {}
+
+pub struct MacWindow(Arc<Mutex<MacWindowState>>);
 
 impl MacWindow {
     pub fn open(
         handle: AnyWindowHandle,
-        options: platform::WindowOptions,
-        executor: Rc<executor::Foreground>,
-        fonts: Arc<dyn platform::FontSystem>,
+        options: WindowOptions,
+        draw: DrawWindow,
+        executor: ForegroundExecutor,
     ) -> Self {
         unsafe {
             let pool = NSAutoreleasePool::new(nil);
@@ -455,19 +481,40 @@ impl MacWindow {
                     msg_send![PANEL_CLASS, alloc]
                 }
             };
+
+            let display = options
+                .display_id
+                .and_then(|display_id| MacDisplay::all().find(|display| display.id() == display_id))
+                .unwrap_or_else(MacDisplay::primary);
+
+            let mut target_screen = nil;
+            let screens = NSScreen::screens(nil);
+            let count: u64 = cocoa::foundation::NSArray::count(screens);
+            for i in 0..count {
+                let screen = cocoa::foundation::NSArray::objectAtIndex(screens, i);
+                let device_description = NSScreen::deviceDescription(screen);
+                let screen_number_key: id = NSString::alloc(nil).init_str("NSScreenNumber");
+                let screen_number = device_description.objectForKey_(screen_number_key);
+                let screen_number: NSUInteger = msg_send![screen_number, unsignedIntegerValue];
+                if screen_number as u32 == display.id().0 {
+                    target_screen = screen;
+                    break;
+                }
+            }
+
             let native_window = native_window.initWithContentRect_styleMask_backing_defer_screen_(
                 NSRect::new(NSPoint::new(0., 0.), NSSize::new(1024., 768.)),
                 style_mask,
                 NSBackingStoreBuffered,
                 NO,
-                options
-                    .screen
-                    .and_then(|screen| {
-                        Some(screen.as_any().downcast_ref::<Screen>()?.native_screen)
-                    })
-                    .unwrap_or(nil),
+                target_screen,
             );
             assert!(!native_window.is_null());
+            let () = msg_send![
+                native_window,
+                registerForDraggedTypes:
+                    NSArray::arrayWithObject(nil, NSFilenamesPboardType)
+            ];
 
             let screen = native_window.screen();
             match options.bounds {
@@ -477,14 +524,14 @@ impl MacWindow {
                 WindowBounds::Maximized => {
                     native_window.setFrame_display_(screen.visibleFrame(), YES);
                 }
-                WindowBounds::Fixed(rect) => {
-                    let bounds = Screen::screen_rect_to_native(rect);
-                    let screen_bounds = screen.visibleFrame();
-                    if bounds.intersects(screen_bounds) {
-                        native_window.setFrame_display_(bounds, YES);
+                WindowBounds::Fixed(bounds) => {
+                    let display_bounds = display.bounds();
+                    let frame = if bounds.intersects(&display_bounds) {
+                        display_bounds_to_native(bounds)
                     } else {
-                        native_window.setFrame_display_(screen_bounds, YES);
-                    }
+                        display_bounds_to_native(display_bounds)
+                    };
+                    native_window.setFrame_display_(mem::transmute::<CGRect, NSRect>(frame), YES);
                 }
             }
 
@@ -493,25 +540,25 @@ impl MacWindow {
 
             assert!(!native_view.is_null());
 
-            let window = Self(Rc::new(RefCell::new(WindowState {
+            let window = Self(Arc::new(Mutex::new(MacWindowState {
                 handle,
+                executor,
                 native_window,
+                renderer: MetalRenderer::new(true),
+                draw: Some(draw),
                 kind: options.kind,
                 event_callback: None,
-                resize_callback: None,
-                should_close_callback: None,
-                close_callback: None,
                 activate_callback: None,
+                resize_callback: None,
                 fullscreen_callback: None,
                 moved_callback: None,
+                should_close_callback: None,
+                close_callback: None,
                 appearance_changed_callback: None,
                 input_handler: None,
                 pending_key_down: None,
                 last_key_equivalent: None,
                 synthetic_drag_counter: 0,
-                executor,
-                scene_to_render: Default::default(),
-                renderer: Renderer::new(true, fonts),
                 last_fresh_keydown: None,
                 traffic_light_position: options
                     .titlebar
@@ -524,15 +571,19 @@ impl MacWindow {
 
             (*native_window).set_ivar(
                 WINDOW_STATE_IVAR,
-                Rc::into_raw(window.0.clone()) as *const c_void,
+                Arc::into_raw(window.0.clone()) as *const c_void,
             );
             native_window.setDelegate_(native_window);
             (*native_view).set_ivar(
                 WINDOW_STATE_IVAR,
-                Rc::into_raw(window.0.clone()) as *const c_void,
+                Arc::into_raw(window.0.clone()) as *const c_void,
             );
 
-            if let Some(title) = options.titlebar.as_ref().and_then(|t| t.title) {
+            if let Some(title) = options
+                .titlebar
+                .as_ref()
+                .and_then(|t| t.title.as_ref().map(AsRef::as_ref))
+            {
                 native_window.setTitle_(NSString::alloc(nil).init_str(title));
             }
 
@@ -604,19 +655,19 @@ impl MacWindow {
                 native_window.orderFront_(nil);
             }
 
-            window.0.borrow().move_traffic_light();
+            window.0.lock().move_traffic_light();
             pool.drain();
 
             window
         }
     }
 
-    pub fn main_window() -> Option<AnyWindowHandle> {
+    pub fn active_window() -> Option<AnyWindowHandle> {
         unsafe {
             let app = NSApplication::sharedApplication(nil);
             let main_window: id = msg_send![app, mainWindow];
             if msg_send![main_window, isKindOfClass: WINDOW_CLASS] {
-                let handle = get_window_state(&*main_window).borrow().handle;
+                let handle = get_window_state(&*main_window).lock().handle;
                 Some(handle)
             } else {
                 None
@@ -627,11 +678,14 @@ impl MacWindow {
 
 impl Drop for MacWindow {
     fn drop(&mut self) {
-        let this = self.0.borrow();
+        let this = self.0.lock();
         let window = this.native_window;
         this.executor
             .spawn(async move {
                 unsafe {
+                    // todo!() this panic()s when you click the red close button
+                    // unless should_close returns false.
+                    // (luckliy in zed it always returns false)
                     window.close();
                 }
             })
@@ -639,62 +693,88 @@ impl Drop for MacWindow {
     }
 }
 
-impl platform::Window for MacWindow {
+impl PlatformWindow for MacWindow {
     fn bounds(&self) -> WindowBounds {
-        self.0.as_ref().borrow().bounds()
+        self.0.as_ref().lock().bounds()
     }
 
-    fn content_size(&self) -> Vector2F {
-        self.0.as_ref().borrow().content_size()
+    fn content_size(&self) -> Size<Pixels> {
+        self.0.as_ref().lock().content_size()
     }
 
     fn scale_factor(&self) -> f32 {
-        self.0.as_ref().borrow().scale_factor()
+        self.0.as_ref().lock().scale_factor()
     }
 
-    fn titlebar_height(&self) -> f32 {
-        self.0.as_ref().borrow().titlebar_height()
+    fn titlebar_height(&self) -> Pixels {
+        self.0.as_ref().lock().titlebar_height()
     }
 
-    fn appearance(&self) -> platform::Appearance {
+    fn appearance(&self) -> WindowAppearance {
         unsafe {
-            let appearance: id = msg_send![self.0.borrow().native_window, effectiveAppearance];
-            platform::Appearance::from_native(appearance)
+            let appearance: id = msg_send![self.0.lock().native_window, effectiveAppearance];
+            WindowAppearance::from_native(appearance)
         }
     }
 
-    fn screen(&self) -> Rc<dyn platform::Screen> {
+    fn display(&self) -> Rc<dyn PlatformDisplay> {
         unsafe {
-            Rc::new(Screen {
-                native_screen: self.0.as_ref().borrow().native_window.screen(),
-            })
+            let screen = self.0.lock().native_window.screen();
+            let device_description: id = msg_send![screen, deviceDescription];
+            let screen_number: id = NSDictionary::valueForKey_(
+                device_description,
+                NSString::alloc(nil).init_str("NSScreenNumber"),
+            );
+
+            let screen_number: u32 = msg_send![screen_number, unsignedIntValue];
+
+            Rc::new(MacDisplay(screen_number))
         }
     }
 
-    fn mouse_position(&self) -> Vector2F {
+    fn mouse_position(&self) -> Point<Pixels> {
         let position = unsafe {
             self.0
-                .borrow()
+                .lock()
                 .native_window
                 .mouseLocationOutsideOfEventStream()
         };
-        convert_mouse_position(position, self.content_size().y())
+        convert_mouse_position(position, self.content_size().height)
+    }
+
+    fn modifiers(&self) -> Modifiers {
+        unsafe {
+            let modifiers: NSEventModifierFlags = msg_send![class!(NSEvent), modifierFlags];
+
+            let control = modifiers.contains(NSEventModifierFlags::NSControlKeyMask);
+            let alt = modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask);
+            let shift = modifiers.contains(NSEventModifierFlags::NSShiftKeyMask);
+            let command = modifiers.contains(NSEventModifierFlags::NSCommandKeyMask);
+            let function = modifiers.contains(NSEventModifierFlags::NSFunctionKeyMask);
+
+            Modifiers {
+                control,
+                alt,
+                shift,
+                command,
+                function,
+            }
+        }
     }
 
     fn as_any_mut(&mut self) -> &mut dyn Any {
         self
     }
 
-    fn set_input_handler(&mut self, input_handler: Box<dyn InputHandler>) {
-        self.0.as_ref().borrow_mut().input_handler = Some(input_handler);
+    fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>) {
+        self.0.as_ref().lock().input_handler = Some(input_handler);
+    }
+
+    fn clear_input_handler(&mut self) {
+        self.0.as_ref().lock().input_handler = None;
     }
 
-    fn prompt(
-        &self,
-        level: platform::PromptLevel,
-        msg: &str,
-        answers: &[&str],
-    ) -> oneshot::Receiver<usize> {
+    fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver<usize> {
         // macOs applies overrides to modal window buttons after they are added.
         // Two most important for this logic are:
         // * Buttons with "Cancel" title will be displayed as the last buttons in the modal
@@ -724,9 +804,9 @@ impl platform::Window for MacWindow {
             let alert: id = msg_send![class!(NSAlert), alloc];
             let alert: id = msg_send![alert, init];
             let alert_style = match level {
-                platform::PromptLevel::Info => 1,
-                platform::PromptLevel::Warning => 0,
-                platform::PromptLevel::Critical => 2,
+                PromptLevel::Info => 1,
+                PromptLevel::Warning => 0,
+                PromptLevel::Critical => 2,
             };
             let _: () = msg_send![alert, setAlertStyle: alert_style];
             let _: () = msg_send![alert, setMessageText: ns_string(msg)];
@@ -747,15 +827,14 @@ impl platform::Window for MacWindow {
             let (done_tx, done_rx) = oneshot::channel();
             let done_tx = Cell::new(Some(done_tx));
             let block = ConcreteBlock::new(move |answer: NSInteger| {
-                if let Some(mut done_tx) = done_tx.take() {
-                    let _ = postage::sink::Sink::try_send(&mut done_tx, answer.try_into().unwrap());
+                if let Some(done_tx) = done_tx.take() {
+                    let _ = done_tx.send(answer.try_into().unwrap());
                 }
             });
             let block = block.copy();
-            let native_window = self.0.borrow().native_window;
-            self.0
-                .borrow()
-                .executor
+            let native_window = self.0.lock().native_window;
+            let executor = self.0.lock().executor.clone();
+            executor
                 .spawn(async move {
                     let _: () = msg_send![
                         alert,
@@ -770,10 +849,9 @@ impl platform::Window for MacWindow {
     }
 
     fn activate(&self) {
-        let window = self.0.borrow().native_window;
-        self.0
-            .borrow()
-            .executor
+        let window = self.0.lock().native_window;
+        let executor = self.0.lock().executor.clone();
+        executor
             .spawn(async move {
                 unsafe {
                     let _: () = msg_send![window, makeKeyAndOrderFront: nil];
@@ -785,42 +863,47 @@ impl platform::Window for MacWindow {
     fn set_title(&mut self, title: &str) {
         unsafe {
             let app = NSApplication::sharedApplication(nil);
-            let window = self.0.borrow().native_window;
+            let window = self.0.lock().native_window;
             let title = ns_string(title);
             let _: () = msg_send![app, changeWindowsItem:window title:title filename:false];
             let _: () = msg_send![window, setTitle: title];
-            self.0.borrow().move_traffic_light();
+            self.0.lock().move_traffic_light();
         }
     }
 
     fn set_edited(&mut self, edited: bool) {
         unsafe {
-            let window = self.0.borrow().native_window;
+            let window = self.0.lock().native_window;
             msg_send![window, setDocumentEdited: edited as BOOL]
         }
 
         // Changing the document edited state resets the traffic light position,
         // so we have to move it again.
-        self.0.borrow().move_traffic_light();
+        self.0.lock().move_traffic_light();
     }
 
     fn show_character_palette(&self) {
-        unsafe {
-            let app = NSApplication::sharedApplication(nil);
-            let window = self.0.borrow().native_window;
-            let _: () = msg_send![app, orderFrontCharacterPalette: window];
-        }
+        let this = self.0.lock();
+        let window = this.native_window;
+        this.executor
+            .spawn(async move {
+                unsafe {
+                    let app = NSApplication::sharedApplication(nil);
+                    let _: () = msg_send![app, orderFrontCharacterPalette: window];
+                }
+            })
+            .detach();
     }
 
     fn minimize(&self) {
-        let window = self.0.borrow().native_window;
+        let window = self.0.lock().native_window;
         unsafe {
             window.miniaturize_(nil);
         }
     }
 
     fn zoom(&self) {
-        let this = self.0.borrow();
+        let this = self.0.lock();
         let window = this.native_window;
         this.executor
             .spawn(async move {
@@ -831,12 +914,8 @@ impl platform::Window for MacWindow {
             .detach();
     }
 
-    fn present_scene(&mut self, scene: Scene) {
-        self.0.as_ref().borrow_mut().present_scene(scene);
-    }
-
     fn toggle_full_screen(&self) {
-        let this = self.0.borrow();
+        let this = self.0.lock();
         let window = this.native_window;
         this.executor
             .spawn(async move {
@@ -847,50 +926,47 @@ impl platform::Window for MacWindow {
             .detach();
     }
 
-    fn on_event(&mut self, callback: Box<dyn FnMut(Event) -> bool>) {
-        self.0.as_ref().borrow_mut().event_callback = Some(callback);
+    fn on_input(&self, callback: Box<dyn FnMut(InputEvent) -> bool>) {
+        self.0.as_ref().lock().event_callback = Some(callback);
     }
 
-    fn on_active_status_change(&mut self, callback: Box<dyn FnMut(bool)>) {
-        self.0.as_ref().borrow_mut().activate_callback = Some(callback);
+    fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
+        self.0.as_ref().lock().activate_callback = Some(callback);
     }
 
-    fn on_resize(&mut self, callback: Box<dyn FnMut()>) {
-        self.0.as_ref().borrow_mut().resize_callback = Some(callback);
+    fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
+        self.0.as_ref().lock().resize_callback = Some(callback);
     }
 
-    fn on_fullscreen(&mut self, callback: Box<dyn FnMut(bool)>) {
-        self.0.as_ref().borrow_mut().fullscreen_callback = Some(callback);
+    fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>) {
+        self.0.as_ref().lock().fullscreen_callback = Some(callback);
     }
 
-    fn on_moved(&mut self, callback: Box<dyn FnMut()>) {
-        self.0.as_ref().borrow_mut().moved_callback = Some(callback);
+    fn on_moved(&self, callback: Box<dyn FnMut()>) {
+        self.0.as_ref().lock().moved_callback = Some(callback);
     }
 
-    fn on_should_close(&mut self, callback: Box<dyn FnMut() -> bool>) {
-        self.0.as_ref().borrow_mut().should_close_callback = Some(callback);
+    fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
+        self.0.as_ref().lock().should_close_callback = Some(callback);
     }
 
-    fn on_close(&mut self, callback: Box<dyn FnOnce()>) {
-        self.0.as_ref().borrow_mut().close_callback = Some(callback);
+    fn on_close(&self, callback: Box<dyn FnOnce()>) {
+        self.0.as_ref().lock().close_callback = Some(callback);
     }
 
-    fn on_appearance_changed(&mut self, callback: Box<dyn FnMut()>) {
-        self.0.borrow_mut().appearance_changed_callback = Some(callback);
+    fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
+        self.0.lock().appearance_changed_callback = Some(callback);
     }
 
-    fn is_topmost_for_position(&self, position: Vector2F) -> bool {
-        let self_borrow = self.0.borrow();
+    fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool {
+        let self_borrow = self.0.lock();
         let self_handle = self_borrow.handle;
 
         unsafe {
             let app = NSApplication::sharedApplication(nil);
 
             // Convert back to screen coordinates
-            let screen_point = position.to_screen_ns_point(
-                self_borrow.native_window,
-                self_borrow.content_size().y() as f64,
-            );
+            let screen_point = self_borrow.to_screen_ns_point(position);
 
             let window_number: NSInteger = msg_send![class!(NSWindow), windowNumberAtPoint:screen_point belowWindowWithWindowNumber:0];
             let top_most_window: id = msg_send![app, windowWithWindowNumber: window_number];
@@ -898,7 +974,7 @@ impl platform::Window for MacWindow {
             let is_panel: BOOL = msg_send![top_most_window, isKindOfClass: PANEL_CLASS];
             let is_window: BOOL = msg_send![top_most_window, isKindOfClass: WINDOW_CLASS];
             if is_panel == YES || is_window == YES {
-                let topmost_window = get_window_state(&*top_most_window).borrow().handle;
+                let topmost_window = get_window_state(&*top_most_window).lock().handle;
                 topmost_window == self_handle
             } else {
                 // Someone else's window is on top
@@ -906,6 +982,17 @@ impl platform::Window for MacWindow {
             }
         }
     }
+
+    fn invalidate(&self) {
+        let this = self.0.lock();
+        unsafe {
+            let _: () = msg_send![this.native_window.contentView(), setNeedsDisplay: YES];
+        }
+    }
+
+    fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
+        self.0.lock().renderer.sprite_atlas().clone()
+    }
 }
 
 fn get_scale_factor(native_window: id) -> f32 {
@@ -915,9 +1002,9 @@ fn get_scale_factor(native_window: id) -> f32 {
     }
 }
 
-unsafe fn get_window_state(object: &Object) -> Rc<RefCell<WindowState>> {
+unsafe fn get_window_state(object: &Object) -> Arc<Mutex<MacWindowState>> {
     let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
-    let rc1 = Rc::from_raw(raw as *mut RefCell<WindowState>);
+    let rc1 = Arc::from_raw(raw as *mut Mutex<MacWindowState>);
     let rc2 = rc1.clone();
     mem::forget(rc1);
     rc2
@@ -925,7 +1012,7 @@ unsafe fn get_window_state(object: &Object) -> Rc<RefCell<WindowState>> {
 
 unsafe fn drop_window_state(object: &Object) {
     let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
-    Rc::from_raw(raw as *mut RefCell<WindowState>);
+    Rc::from_raw(raw as *mut RefCell<MacWindowState>);
 }
 
 extern "C" fn yes(_: &Object, _: Sel) -> BOOL {
@@ -956,35 +1043,35 @@ extern "C" fn handle_key_down(this: &Object, _: Sel, native_event: id) {
 
 extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent: bool) -> BOOL {
     let window_state = unsafe { get_window_state(this) };
-    let mut window_state_borrow = window_state.as_ref().borrow_mut();
+    let mut lock = window_state.as_ref().lock();
 
-    let window_height = window_state_borrow.content_size().y();
-    let event = unsafe { Event::from_native(native_event, Some(window_height)) };
+    let window_height = lock.content_size().height;
+    let event = unsafe { InputEvent::from_native(native_event, Some(window_height)) };
 
-    if let Some(Event::KeyDown(event)) = event {
+    if let Some(InputEvent::KeyDown(event)) = event {
         // For certain keystrokes, macOS will first dispatch a "key equivalent" event.
         // If that event isn't handled, it will then dispatch a "key down" event. GPUI
         // makes no distinction between these two types of events, so we need to ignore
         // the "key down" event if we've already just processed its "key equivalent" version.
         if key_equivalent {
-            window_state_borrow.last_key_equivalent = Some(event.clone());
-        } else if window_state_borrow.last_key_equivalent.take().as_ref() == Some(&event) {
+            lock.last_key_equivalent = Some(event.clone());
+        } else if lock.last_key_equivalent.take().as_ref() == Some(&event) {
             return NO;
         }
 
         let keydown = event.keystroke.clone();
-        let fn_modifier = keydown.function;
+        let fn_modifier = keydown.modifiers.function;
         // Ignore events from held-down keys after some of the initially-pressed keys
         // were released.
         if event.is_held {
-            if window_state_borrow.last_fresh_keydown.as_ref() != Some(&keydown) {
+            if lock.last_fresh_keydown.as_ref() != Some(&keydown) {
                 return YES;
             }
         } else {
-            window_state_borrow.last_fresh_keydown = Some(keydown);
+            lock.last_fresh_keydown = Some(keydown);
         }
-        window_state_borrow.pending_key_down = Some((event, None));
-        drop(window_state_borrow);
+        lock.pending_key_down = Some((event, None));
+        drop(lock);
 
         // Send the event to the input context for IME handling, unless the `fn` modifier is
         // being pressed.
@@ -996,19 +1083,49 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
         }
 
         let mut handled = false;
-        let mut window_state_borrow = window_state.borrow_mut();
-        let ime_text = window_state_borrow.ime_text.clone();
-        if let Some((event, insert_text)) = window_state_borrow.pending_key_down.take() {
+        let mut lock = window_state.lock();
+        let ime_text = lock.ime_text.clone();
+        if let Some((event, insert_text)) = lock.pending_key_down.take() {
             let is_held = event.is_held;
-            if let Some(mut callback) = window_state_borrow.event_callback.take() {
-                drop(window_state_borrow);
+            if let Some(mut callback) = lock.event_callback.take() {
+                drop(lock);
 
                 let is_composing =
                     with_input_handler(this, |input_handler| input_handler.marked_text_range())
                         .flatten()
                         .is_some();
                 if !is_composing {
-                    handled = callback(Event::KeyDown(event));
+                    // if the IME has changed the key, we'll first emit an event with the character
+                    // generated by the IME system; then fallback to the keystroke if that is not
+                    // handled.
+                    // cases that we have working:
+                    // - " on a brazillian layout by typing <quote><space>
+                    // - ctrl-` on a brazillian layout by typing <ctrl-`>
+                    // - $ on a czech QWERTY layout by typing <alt-4>
+                    // - 4 on a czech QWERTY layout by typing <shift-4>
+                    // - ctrl-4 on a czech QWERTY layout by typing <ctrl-alt-4> (or <ctrl-shift-4>)
+                    if ime_text.is_some() && ime_text.as_ref() != Some(&event.keystroke.key) {
+                        let event_with_ime_text = KeyDownEvent {
+                            is_held: false,
+                            keystroke: Keystroke {
+                                // we match ctrl because some use-cases need it.
+                                // we don't match alt because it's often used to generate the optional character
+                                // we don't match shift because we're not here with letters (usually)
+                                // we don't match cmd/fn because they don't seem to use IME
+                                modifiers: Default::default(),
+                                key: ime_text.clone().unwrap(),
+                                ime_key: None, // todo!("handle IME key")
+                            },
+                        };
+                        handled = callback(InputEvent::KeyDown(event_with_ime_text));
+                    }
+                    if !handled {
+                        // empty key happens when you type a deadkey in input composition.
+                        // (e.g. on a brazillian keyboard typing quote is a deadkey)
+                        if !event.keystroke.key.is_empty() {
+                            handled = callback(InputEvent::KeyDown(event));
+                        }
+                    }
                 }
 
                 if !handled {
@@ -1033,7 +1150,7 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
                     }
                 }
 
-                window_state.borrow_mut().event_callback = Some(callback);
+                window_state.lock().event_callback = Some(callback);
             }
         } else {
             handled = true;
@@ -1047,108 +1164,103 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
 
 extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
     let window_state = unsafe { get_window_state(this) };
-    let weak_window_state = Rc::downgrade(&window_state);
-    let mut window_state_borrow = window_state.as_ref().borrow_mut();
-    let is_active = unsafe { window_state_borrow.native_window.isKeyWindow() == YES };
+    let weak_window_state = Arc::downgrade(&window_state);
+    let mut lock = window_state.as_ref().lock();
+    let is_active = unsafe { lock.native_window.isKeyWindow() == YES };
 
-    let window_height = window_state_borrow.content_size().y();
-    let event = unsafe { Event::from_native(native_event, Some(window_height)) };
+    let window_height = lock.content_size().height;
+    let event = unsafe { InputEvent::from_native(native_event, Some(window_height)) };
 
     if let Some(mut event) = event {
-        let synthesized_second_event = match &mut event {
-            Event::MouseDown(
-                event @ MouseButtonEvent {
+        match &mut event {
+            InputEvent::MouseDown(
+                event @ MouseDownEvent {
                     button: MouseButton::Left,
-                    modifiers: Modifiers { ctrl: true, .. },
+                    modifiers: Modifiers { control: true, .. },
                     ..
                 },
             ) => {
-                *event = MouseButtonEvent {
+                // On mac, a ctrl-left click should be handled as a right click.
+                *event = MouseDownEvent {
                     button: MouseButton::Right,
                     modifiers: Modifiers {
-                        ctrl: false,
+                        control: false,
                         ..event.modifiers
                     },
                     click_count: 1,
                     ..*event
                 };
-
-                Some(Event::MouseUp(MouseButtonEvent {
-                    button: MouseButton::Right,
-                    ..*event
-                }))
             }
 
             // Because we map a ctrl-left_down to a right_down -> right_up let's ignore
             // the ctrl-left_up to avoid having a mismatch in button down/up events if the
             // user is still holding ctrl when releasing the left mouse button
-            Event::MouseUp(MouseButtonEvent {
-                button: MouseButton::Left,
-                modifiers: Modifiers { ctrl: true, .. },
-                ..
-            }) => {
-                window_state_borrow.synthetic_drag_counter += 1;
-                return;
+            InputEvent::MouseUp(
+                event @ MouseUpEvent {
+                    button: MouseButton::Left,
+                    modifiers: Modifiers { control: true, .. },
+                    ..
+                },
+            ) => {
+                *event = MouseUpEvent {
+                    button: MouseButton::Right,
+                    modifiers: Modifiers {
+                        control: false,
+                        ..event.modifiers
+                    },
+                    click_count: 1,
+                    ..*event
+                };
             }
 
-            _ => None,
+            _ => {}
         };
 
         match &event {
-            Event::MouseMoved(
-                event @ MouseMovedEvent {
+            InputEvent::MouseMove(
+                event @ MouseMoveEvent {
                     pressed_button: Some(_),
                     ..
                 },
             ) => {
-                window_state_borrow.synthetic_drag_counter += 1;
-                window_state_borrow
-                    .executor
+                lock.synthetic_drag_counter += 1;
+                let executor = lock.executor.clone();
+                executor
                     .spawn(synthetic_drag(
                         weak_window_state,
-                        window_state_borrow.synthetic_drag_counter,
-                        *event,
+                        lock.synthetic_drag_counter,
+                        event.clone(),
                     ))
                     .detach();
             }
 
-            Event::MouseMoved(_)
-                if !(is_active || window_state_borrow.kind == WindowKind::PopUp) =>
-            {
-                return
-            }
+            InputEvent::MouseMove(_) if !(is_active || lock.kind == WindowKind::PopUp) => return,
 
-            Event::MouseUp(MouseButtonEvent {
-                button: MouseButton::Left,
-                ..
-            }) => {
-                window_state_borrow.synthetic_drag_counter += 1;
+            InputEvent::MouseUp(MouseUpEvent { .. }) => {
+                lock.synthetic_drag_counter += 1;
             }
 
-            Event::ModifiersChanged(ModifiersChangedEvent { modifiers }) => {
+            InputEvent::ModifiersChanged(ModifiersChangedEvent { modifiers }) => {
                 // Only raise modifiers changed event when they have actually changed
-                if let Some(Event::ModifiersChanged(ModifiersChangedEvent {
+                if let Some(InputEvent::ModifiersChanged(ModifiersChangedEvent {
                     modifiers: prev_modifiers,
-                })) = &window_state_borrow.previous_modifiers_changed_event
+                })) = &lock.previous_modifiers_changed_event
                 {
                     if prev_modifiers == modifiers {
                         return;
                     }
                 }
 
-                window_state_borrow.previous_modifiers_changed_event = Some(event.clone());
+                lock.previous_modifiers_changed_event = Some(event.clone());
             }
 
             _ => {}
         }
 
-        if let Some(mut callback) = window_state_borrow.event_callback.take() {
-            drop(window_state_borrow);
+        if let Some(mut callback) = lock.event_callback.take() {
+            drop(lock);
             callback(event);
-            if let Some(event) = synthesized_second_event {
-                callback(event);
-            }
-            window_state.borrow_mut().event_callback = Some(callback);
+            window_state.lock().event_callback = Some(callback);
         }
     }
 }

crates/gpui/src/platform/test.rs 🔗

@@ -1,434 +1,9 @@
-use super::{AppVersion, CursorStyle, WindowBounds};
-use crate::{
-    geometry::{
-        rect::RectF,
-        vector::{vec2f, Vector2F},
-    },
-    keymap_matcher::KeymapMatcher,
-    Action, AnyWindowHandle, ClipboardItem, Menu,
-};
-use anyhow::{anyhow, Result};
-use collections::VecDeque;
-use parking_lot::Mutex;
-use postage::oneshot;
-use std::{
-    any::Any,
-    cell::RefCell,
-    path::{Path, PathBuf},
-    rc::Rc,
-    sync::Arc,
-};
-use time::UtcOffset;
-
-struct Dispatcher;
-
-impl super::Dispatcher for Dispatcher {
-    fn is_main_thread(&self) -> bool {
-        true
-    }
-
-    fn run_on_main_thread(&self, task: async_task::Runnable) {
-        task.run();
-    }
-}
-
-pub fn foreground_platform() -> ForegroundPlatform {
-    ForegroundPlatform::default()
-}
-
-#[derive(Default)]
-pub struct ForegroundPlatform {
-    last_prompt_for_new_path_args: RefCell<Option<(PathBuf, oneshot::Sender<Option<PathBuf>>)>>,
-}
-
-#[cfg(any(test, feature = "test-support"))]
-impl ForegroundPlatform {
-    pub(crate) fn simulate_new_path_selection(
-        &self,
-        result: impl FnOnce(PathBuf) -> Option<PathBuf>,
-    ) {
-        let (dir_path, mut done_tx) = self
-            .last_prompt_for_new_path_args
-            .take()
-            .expect("prompt_for_new_path was not called");
-        let _ = postage::sink::Sink::try_send(&mut done_tx, result(dir_path));
-    }
-
-    pub(crate) fn did_prompt_for_new_path(&self) -> bool {
-        self.last_prompt_for_new_path_args.borrow().is_some()
-    }
-}
-
-impl super::ForegroundPlatform for ForegroundPlatform {
-    fn on_become_active(&self, _: Box<dyn FnMut()>) {}
-    fn on_resign_active(&self, _: Box<dyn FnMut()>) {}
-    fn on_quit(&self, _: Box<dyn FnMut()>) {}
-    fn on_reopen(&self, _: Box<dyn FnMut()>) {}
-    fn on_event(&self, _: Box<dyn FnMut(crate::platform::Event) -> bool>) {}
-    fn on_open_urls(&self, _: Box<dyn FnMut(Vec<String>)>) {}
-
-    fn run(&self, _on_finish_launching: Box<dyn FnOnce()>) {
-        unimplemented!()
-    }
-
-    fn on_menu_command(&self, _: Box<dyn FnMut(&dyn Action)>) {}
-    fn on_validate_menu_command(&self, _: Box<dyn FnMut(&dyn Action) -> bool>) {}
-    fn on_will_open_menu(&self, _: Box<dyn FnMut()>) {}
-    fn set_menus(&self, _: Vec<Menu>, _: &KeymapMatcher) {}
-
-    fn prompt_for_paths(
-        &self,
-        _: super::PathPromptOptions,
-    ) -> oneshot::Receiver<Option<Vec<PathBuf>>> {
-        let (_done_tx, done_rx) = oneshot::channel();
-        done_rx
-    }
-
-    fn prompt_for_new_path(&self, path: &Path) -> oneshot::Receiver<Option<PathBuf>> {
-        let (done_tx, done_rx) = oneshot::channel();
-        *self.last_prompt_for_new_path_args.borrow_mut() = Some((path.to_path_buf(), done_tx));
-        done_rx
-    }
-
-    fn reveal_path(&self, _: &Path) {}
-}
-
-pub fn platform() -> Platform {
-    Platform::new()
-}
-
-pub struct Platform {
-    dispatcher: Arc<dyn super::Dispatcher>,
-    fonts: Arc<dyn super::FontSystem>,
-    current_clipboard_item: Mutex<Option<ClipboardItem>>,
-    cursor: Mutex<CursorStyle>,
-    active_window: Arc<Mutex<Option<AnyWindowHandle>>>,
-    active_screen: Screen,
-}
-
-impl Platform {
-    fn new() -> Self {
-        Self {
-            dispatcher: Arc::new(Dispatcher),
-            fonts: Arc::new(super::current::FontSystem::new()),
-            current_clipboard_item: Default::default(),
-            cursor: Mutex::new(CursorStyle::Arrow),
-            active_window: Default::default(),
-            active_screen: Screen::new(),
-        }
-    }
-}
-
-impl super::Platform for Platform {
-    fn dispatcher(&self) -> Arc<dyn super::Dispatcher> {
-        self.dispatcher.clone()
-    }
-
-    fn fonts(&self) -> std::sync::Arc<dyn super::FontSystem> {
-        self.fonts.clone()
-    }
-
-    fn activate(&self, _ignoring_other_apps: bool) {}
-
-    fn hide(&self) {}
-
-    fn hide_other_apps(&self) {}
-
-    fn unhide_other_apps(&self) {}
-
-    fn quit(&self) {}
-
-    fn screen_by_id(&self, uuid: uuid::Uuid) -> Option<Rc<dyn crate::platform::Screen>> {
-        if self.active_screen.uuid == uuid {
-            Some(Rc::new(self.active_screen.clone()))
-        } else {
-            None
-        }
-    }
-
-    fn screens(&self) -> Vec<Rc<dyn crate::platform::Screen>> {
-        vec![Rc::new(self.active_screen.clone())]
-    }
-
-    fn open_window(
-        &self,
-        handle: AnyWindowHandle,
-        options: super::WindowOptions,
-        _executor: Rc<super::executor::Foreground>,
-    ) -> Box<dyn super::Window> {
-        *self.active_window.lock() = Some(handle);
-        Box::new(Window::new(
-            handle,
-            match options.bounds {
-                WindowBounds::Maximized | WindowBounds::Fullscreen => vec2f(1024., 768.),
-                WindowBounds::Fixed(rect) => rect.size(),
-            },
-            self.active_window.clone(),
-            Rc::new(self.active_screen.clone()),
-        ))
-    }
-
-    fn main_window(&self) -> Option<AnyWindowHandle> {
-        self.active_window.lock().clone()
-    }
-
-    fn add_status_item(&self, handle: AnyWindowHandle) -> Box<dyn crate::platform::Window> {
-        Box::new(Window::new(
-            handle,
-            vec2f(24., 24.),
-            self.active_window.clone(),
-            Rc::new(self.active_screen.clone()),
-        ))
-    }
-
-    fn write_to_clipboard(&self, item: ClipboardItem) {
-        *self.current_clipboard_item.lock() = Some(item);
-    }
-
-    fn read_from_clipboard(&self) -> Option<ClipboardItem> {
-        self.current_clipboard_item.lock().clone()
-    }
-
-    fn open_url(&self, _: &str) {}
-
-    fn write_credentials(&self, _: &str, _: &str, _: &[u8]) -> Result<()> {
-        Ok(())
-    }
-
-    fn read_credentials(&self, _: &str) -> Result<Option<(String, Vec<u8>)>> {
-        Ok(None)
-    }
-
-    fn delete_credentials(&self, _: &str) -> Result<()> {
-        Ok(())
-    }
-
-    fn set_cursor_style(&self, style: CursorStyle) {
-        *self.cursor.lock() = style;
-    }
-
-    fn should_auto_hide_scrollbars(&self) -> bool {
-        false
-    }
-
-    fn local_timezone(&self) -> UtcOffset {
-        UtcOffset::UTC
-    }
-
-    fn path_for_auxiliary_executable(&self, _name: &str) -> Result<PathBuf> {
-        Err(anyhow!("app not running inside a bundle"))
-    }
-
-    fn app_path(&self) -> Result<PathBuf> {
-        Err(anyhow!("app not running inside a bundle"))
-    }
-
-    fn app_version(&self) -> Result<AppVersion> {
-        Ok(AppVersion {
-            major: 1,
-            minor: 0,
-            patch: 0,
-        })
-    }
-
-    fn os_name(&self) -> &'static str {
-        "test"
-    }
-
-    fn os_version(&self) -> Result<AppVersion> {
-        Ok(AppVersion {
-            major: 1,
-            minor: 0,
-            patch: 0,
-        })
-    }
-
-    fn restart(&self) {}
-}
-
-#[derive(Debug, Clone)]
-pub struct Screen {
-    uuid: uuid::Uuid,
-}
-
-impl Screen {
-    fn new() -> Self {
-        Self {
-            uuid: uuid::Uuid::new_v4(),
-        }
-    }
-}
-
-impl super::Screen for Screen {
-    fn as_any(&self) -> &dyn Any {
-        self
-    }
-
-    fn bounds(&self) -> RectF {
-        RectF::new(Vector2F::zero(), Vector2F::new(1920., 1080.))
-    }
-
-    fn content_bounds(&self) -> RectF {
-        RectF::new(Vector2F::zero(), Vector2F::new(1920., 1080.))
-    }
-
-    fn display_uuid(&self) -> Option<uuid::Uuid> {
-        Some(self.uuid)
-    }
-}
-
-pub struct Window {
-    handle: AnyWindowHandle,
-    pub(crate) size: Vector2F,
-    scale_factor: f32,
-    current_scene: Option<crate::Scene>,
-    event_handlers: Vec<Box<dyn FnMut(super::Event) -> bool>>,
-    pub(crate) resize_handlers: Vec<Box<dyn FnMut()>>,
-    pub(crate) moved_handlers: Vec<Box<dyn FnMut()>>,
-    close_handlers: Vec<Box<dyn FnOnce()>>,
-    fullscreen_handlers: Vec<Box<dyn FnMut(bool)>>,
-    pub(crate) active_status_change_handlers: Vec<Box<dyn FnMut(bool)>>,
-    pub(crate) should_close_handler: Option<Box<dyn FnMut() -> bool>>,
-    pub(crate) title: Option<String>,
-    pub(crate) edited: bool,
-    pub(crate) pending_prompts: RefCell<VecDeque<oneshot::Sender<usize>>>,
-    active_window: Arc<Mutex<Option<AnyWindowHandle>>>,
-    screen: Rc<Screen>,
-}
-
-impl Window {
-    pub fn new(
-        handle: AnyWindowHandle,
-        size: Vector2F,
-        active_window: Arc<Mutex<Option<AnyWindowHandle>>>,
-        screen: Rc<Screen>,
-    ) -> Self {
-        Self {
-            handle,
-            size,
-            event_handlers: Default::default(),
-            resize_handlers: Default::default(),
-            moved_handlers: Default::default(),
-            close_handlers: Default::default(),
-            should_close_handler: Default::default(),
-            active_status_change_handlers: Default::default(),
-            fullscreen_handlers: Default::default(),
-            scale_factor: 1.0,
-            current_scene: None,
-            title: None,
-            edited: false,
-            pending_prompts: Default::default(),
-            active_window,
-            screen,
-        }
-    }
-
-    pub fn title(&self) -> Option<String> {
-        self.title.clone()
-    }
-}
-
-impl super::Window for Window {
-    fn bounds(&self) -> WindowBounds {
-        WindowBounds::Fixed(RectF::new(Vector2F::zero(), self.size))
-    }
-
-    fn content_size(&self) -> Vector2F {
-        self.size
-    }
-
-    fn scale_factor(&self) -> f32 {
-        self.scale_factor
-    }
-
-    fn titlebar_height(&self) -> f32 {
-        24.
-    }
-
-    fn appearance(&self) -> crate::platform::Appearance {
-        crate::platform::Appearance::Light
-    }
-
-    fn screen(&self) -> Rc<dyn crate::platform::Screen> {
-        self.screen.clone()
-    }
-
-    fn mouse_position(&self) -> Vector2F {
-        Vector2F::zero()
-    }
-
-    fn as_any_mut(&mut self) -> &mut dyn Any {
-        self
-    }
-
-    fn set_input_handler(&mut self, _: Box<dyn crate::platform::InputHandler>) {}
-
-    fn prompt(
-        &self,
-        _: crate::platform::PromptLevel,
-        _: &str,
-        _: &[&str],
-    ) -> oneshot::Receiver<usize> {
-        let (done_tx, done_rx) = oneshot::channel();
-        self.pending_prompts.borrow_mut().push_back(done_tx);
-        done_rx
-    }
-
-    fn activate(&self) {
-        *self.active_window.lock() = Some(self.handle);
-    }
-
-    fn set_title(&mut self, title: &str) {
-        self.title = Some(title.to_string())
-    }
-
-    fn set_edited(&mut self, edited: bool) {
-        self.edited = edited;
-    }
-
-    fn show_character_palette(&self) {}
-
-    fn minimize(&self) {}
-
-    fn zoom(&self) {}
-
-    fn present_scene(&mut self, scene: crate::Scene) {
-        self.current_scene = Some(scene);
-    }
-
-    fn toggle_full_screen(&self) {}
-
-    fn on_event(&mut self, callback: Box<dyn FnMut(crate::platform::Event) -> bool>) {
-        self.event_handlers.push(callback);
-    }
-
-    fn on_active_status_change(&mut self, callback: Box<dyn FnMut(bool)>) {
-        self.active_status_change_handlers.push(callback);
-    }
-
-    fn on_resize(&mut self, callback: Box<dyn FnMut()>) {
-        self.resize_handlers.push(callback);
-    }
-
-    fn on_fullscreen(&mut self, callback: Box<dyn FnMut(bool)>) {
-        self.fullscreen_handlers.push(callback)
-    }
-
-    fn on_moved(&mut self, callback: Box<dyn FnMut()>) {
-        self.moved_handlers.push(callback);
-    }
-
-    fn on_should_close(&mut self, callback: Box<dyn FnMut() -> bool>) {
-        self.should_close_handler = Some(callback);
-    }
-
-    fn on_close(&mut self, callback: Box<dyn FnOnce()>) {
-        self.close_handlers.push(callback);
-    }
-
-    fn on_appearance_changed(&mut self, _: Box<dyn FnMut()>) {}
-
-    fn is_topmost_for_position(&self, _position: Vector2F) -> bool {
-        true
-    }
-}
+mod dispatcher;
+mod display;
+mod platform;
+mod window;
+
+pub use dispatcher::*;
+pub use display::*;
+pub use platform::*;
+pub use window::*;

crates/gpui/src/scene.rs 🔗

@@ -1,565 +1,778 @@
-mod mouse_event;
-mod mouse_region;
-mod region;
-
-#[cfg(debug_assertions)]
-use collections::HashSet;
-use derive_more::Mul;
-use schemars::JsonSchema;
-use serde::Deserialize;
-use serde_derive::Serialize;
-use std::{
-    any::{Any, TypeId},
-    borrow::Cow,
-    rc::Rc,
-    sync::Arc,
-};
-
 use crate::{
-    color::Color,
-    fonts::{FontId, GlyphId},
-    geometry::{rect::RectF, vector::Vector2F},
-    platform::{current::Surface, CursorStyle},
-    ImageData, WindowContext,
+    point, AtlasTextureId, AtlasTile, Bounds, ContentMask, Corners, Edges, Hsla, Pixels, Point,
+    ScaledPixels, StackingOrder,
 };
-pub use mouse_event::*;
-pub use mouse_region::*;
+use collections::BTreeMap;
+use std::{fmt::Debug, iter::Peekable, mem, slice};
 
-pub struct SceneBuilder {
-    stacking_contexts: Vec<StackingContext>,
-    active_stacking_context_stack: Vec<usize>,
-    /// Used by the gpui2 crate.
-    pub event_handlers: Vec<EventHandler>,
-    #[cfg(debug_assertions)]
-    mouse_region_ids: HashSet<MouseRegionId>,
-}
+// Exported to metal
+pub(crate) type PointF = Point<f32>;
+#[allow(non_camel_case_types, unused)]
+pub(crate) type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
 
-pub struct Scene {
-    scale_factor: f32,
-    stacking_contexts: Vec<StackingContext>,
-    event_handlers: Vec<EventHandler>,
-}
+pub type LayerId = u32;
 
-struct StackingContext {
-    layers: Vec<Layer>,
-    active_layer_stack: Vec<usize>,
-    z_index: usize,
-}
+pub type DrawOrder = u32;
 
 #[derive(Default)]
-pub struct Layer {
-    clip_bounds: Option<RectF>,
+pub(crate) struct SceneBuilder {
+    last_order: Option<(StackingOrder, LayerId)>,
+    layers_by_order: BTreeMap<StackingOrder, LayerId>,
+    shadows: Vec<Shadow>,
     quads: Vec<Quad>,
+    paths: Vec<Path<ScaledPixels>>,
     underlines: Vec<Underline>,
-    images: Vec<Image>,
+    monochrome_sprites: Vec<MonochromeSprite>,
+    polychrome_sprites: Vec<PolychromeSprite>,
     surfaces: Vec<Surface>,
-    shadows: Vec<Shadow>,
-    glyphs: Vec<Glyph>,
-    image_glyphs: Vec<ImageGlyph>,
-    icons: Vec<Icon>,
-    paths: Vec<Path>,
-    cursor_regions: Vec<CursorRegion>,
-    mouse_regions: Vec<MouseRegion>,
-}
-
-#[derive(Copy, Clone)]
-pub struct CursorRegion {
-    pub bounds: RectF,
-    pub style: CursorStyle,
 }
 
-#[derive(Default, Debug)]
-pub struct Quad {
-    pub bounds: RectF,
-    pub background: Option<Color>,
-    pub border: Border,
-    pub corner_radii: CornerRadii,
-}
-
-#[derive(Default, Debug, Mul, Clone, Copy, Serialize, JsonSchema)]
-pub struct CornerRadii {
-    pub top_left: f32,
-    pub top_right: f32,
-    pub bottom_right: f32,
-    pub bottom_left: f32,
-}
-
-impl<'de> Deserialize<'de> for CornerRadii {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: serde::Deserializer<'de>,
-    {
-        #[derive(Deserialize)]
-        pub struct CornerRadiiHelper {
-            pub top_left: Option<f32>,
-            pub top_right: Option<f32>,
-            pub bottom_right: Option<f32>,
-            pub bottom_left: Option<f32>,
+impl SceneBuilder {
+    pub fn build(&mut self) -> Scene {
+        let mut orders = vec![0; self.layers_by_order.len()];
+        for (ix, layer_id) in self.layers_by_order.values().enumerate() {
+            orders[*layer_id as usize] = ix as u32;
         }
+        self.layers_by_order.clear();
+        self.last_order = None;
 
-        #[derive(Deserialize)]
-        #[serde(untagged)]
-        enum RadiusOrRadii {
-            Radius(f32),
-            Radii(CornerRadiiHelper),
+        for shadow in &mut self.shadows {
+            shadow.order = orders[shadow.order as usize];
         }
+        self.shadows.sort_by_key(|shadow| shadow.order);
 
-        let json = RadiusOrRadii::deserialize(deserializer)?;
-
-        let result = match json {
-            RadiusOrRadii::Radius(radius) => CornerRadii::from(radius),
-            RadiusOrRadii::Radii(CornerRadiiHelper {
-                top_left,
-                top_right,
-                bottom_right,
-                bottom_left,
-            }) => CornerRadii {
-                top_left: top_left.unwrap_or(0.0),
-                top_right: top_right.unwrap_or(0.0),
-                bottom_right: bottom_right.unwrap_or(0.0),
-                bottom_left: bottom_left.unwrap_or(0.0),
-            },
-        };
+        for quad in &mut self.quads {
+            quad.order = orders[quad.order as usize];
+        }
+        self.quads.sort_by_key(|quad| quad.order);
 
-        Ok(result)
-    }
-}
+        for path in &mut self.paths {
+            path.order = orders[path.order as usize];
+        }
+        self.paths.sort_by_key(|path| path.order);
 
-impl From<f32> for CornerRadii {
-    fn from(radius: f32) -> Self {
-        Self {
-            top_left: radius,
-            top_right: radius,
-            bottom_right: radius,
-            bottom_left: radius,
+        for underline in &mut self.underlines {
+            underline.order = orders[underline.order as usize];
         }
-    }
-}
+        self.underlines.sort_by_key(|underline| underline.order);
 
-#[derive(Debug)]
-pub struct Shadow {
-    pub bounds: RectF,
-    pub corner_radii: CornerRadii,
-    pub sigma: f32,
-    pub color: Color,
-}
+        for monochrome_sprite in &mut self.monochrome_sprites {
+            monochrome_sprite.order = orders[monochrome_sprite.order as usize];
+        }
+        self.monochrome_sprites.sort_by_key(|sprite| sprite.order);
 
-#[derive(Debug, Clone, Copy)]
-pub struct Glyph {
-    pub font_id: FontId,
-    pub font_size: f32,
-    pub id: GlyphId,
-    pub origin: Vector2F,
-    pub color: Color,
-}
+        for polychrome_sprite in &mut self.polychrome_sprites {
+            polychrome_sprite.order = orders[polychrome_sprite.order as usize];
+        }
+        self.polychrome_sprites.sort_by_key(|sprite| sprite.order);
 
-#[derive(Debug)]
-pub struct ImageGlyph {
-    pub font_id: FontId,
-    pub font_size: f32,
-    pub id: GlyphId,
-    pub origin: Vector2F,
-}
+        for surface in &mut self.surfaces {
+            surface.order = orders[surface.order as usize];
+        }
+        self.surfaces.sort_by_key(|surface| surface.order);
 
-pub struct Icon {
-    pub bounds: RectF,
-    pub svg: usvg::Tree,
-    pub path: Cow<'static, str>,
-    pub color: Color,
-}
+        Scene {
+            shadows: mem::take(&mut self.shadows),
+            quads: mem::take(&mut self.quads),
+            paths: mem::take(&mut self.paths),
+            underlines: mem::take(&mut self.underlines),
+            monochrome_sprites: mem::take(&mut self.monochrome_sprites),
+            polychrome_sprites: mem::take(&mut self.polychrome_sprites),
+            surfaces: mem::take(&mut self.surfaces),
+        }
+    }
 
-#[derive(Clone, Copy, Default, Debug)]
-pub struct Border {
-    pub color: Color,
-    pub top: f32,
-    pub right: f32,
-    pub bottom: f32,
-    pub left: f32,
-}
+    pub fn insert(&mut self, order: &StackingOrder, primitive: impl Into<Primitive>) {
+        let primitive = primitive.into();
+        let clipped_bounds = primitive
+            .bounds()
+            .intersect(&primitive.content_mask().bounds);
+        if clipped_bounds.size.width <= ScaledPixels(0.)
+            || clipped_bounds.size.height <= ScaledPixels(0.)
+        {
+            return;
+        }
 
-#[derive(Clone, Copy, Default, Debug)]
-pub struct Underline {
-    pub origin: Vector2F,
-    pub width: f32,
-    pub thickness: f32,
-    pub color: Color,
-    pub squiggly: bool,
-}
+        let layer_id = self.layer_id_for_order(order);
+        match primitive {
+            Primitive::Shadow(mut shadow) => {
+                shadow.order = layer_id;
+                self.shadows.push(shadow);
+            }
+            Primitive::Quad(mut quad) => {
+                quad.order = layer_id;
+                self.quads.push(quad);
+            }
+            Primitive::Path(mut path) => {
+                path.order = layer_id;
+                path.id = PathId(self.paths.len());
+                self.paths.push(path);
+            }
+            Primitive::Underline(mut underline) => {
+                underline.order = layer_id;
+                self.underlines.push(underline);
+            }
+            Primitive::MonochromeSprite(mut sprite) => {
+                sprite.order = layer_id;
+                self.monochrome_sprites.push(sprite);
+            }
+            Primitive::PolychromeSprite(mut sprite) => {
+                sprite.order = layer_id;
+                self.polychrome_sprites.push(sprite);
+            }
+            Primitive::Surface(mut surface) => {
+                surface.order = layer_id;
+                self.surfaces.push(surface);
+            }
+        }
+    }
 
-#[derive(Debug)]
-pub struct Path {
-    pub bounds: RectF,
-    pub color: Color,
-    pub vertices: Vec<PathVertex>,
-}
+    fn layer_id_for_order(&mut self, order: &StackingOrder) -> u32 {
+        if let Some((last_order, last_layer_id)) = self.last_order.as_ref() {
+            if last_order == order {
+                return *last_layer_id;
+            }
+        };
 
-#[derive(Debug)]
-pub struct PathVertex {
-    pub xy_position: Vector2F,
-    pub st_position: Vector2F,
+        let layer_id = if let Some(layer_id) = self.layers_by_order.get(order) {
+            *layer_id
+        } else {
+            let next_id = self.layers_by_order.len() as LayerId;
+            self.layers_by_order.insert(order.clone(), next_id);
+            next_id
+        };
+        self.last_order = Some((order.clone(), layer_id));
+        layer_id
+    }
 }
 
-pub struct Image {
-    pub bounds: RectF,
-    pub border: Border,
-    pub corner_radii: CornerRadii,
-    pub grayscale: bool,
-    pub data: Arc<ImageData>,
+pub struct Scene {
+    pub shadows: Vec<Shadow>,
+    pub quads: Vec<Quad>,
+    pub paths: Vec<Path<ScaledPixels>>,
+    pub underlines: Vec<Underline>,
+    pub monochrome_sprites: Vec<MonochromeSprite>,
+    pub polychrome_sprites: Vec<PolychromeSprite>,
+    pub surfaces: Vec<Surface>,
 }
 
 impl Scene {
-    pub fn scale_factor(&self) -> f32 {
-        self.scale_factor
-    }
-
-    pub fn layers(&self) -> impl Iterator<Item = &Layer> {
-        self.stacking_contexts.iter().flat_map(|s| &s.layers)
-    }
-
-    pub fn cursor_regions(&self) -> Vec<CursorRegion> {
-        self.layers()
-            .flat_map(|layer| &layer.cursor_regions)
-            .copied()
-            .collect()
-    }
-
-    pub fn mouse_regions(&self) -> Vec<(MouseRegion, usize)> {
-        self.stacking_contexts
-            .iter()
-            .flat_map(|context| {
-                context
-                    .layers
-                    .iter()
-                    .flat_map(|layer| &layer.mouse_regions)
-                    .map(|region| (region.clone(), context.z_index))
-            })
-            .collect()
+    pub fn paths(&self) -> &[Path<ScaledPixels>] {
+        &self.paths
+    }
+
+    pub(crate) fn batches(&self) -> impl Iterator<Item = PrimitiveBatch> {
+        BatchIterator {
+            shadows: &self.shadows,
+            shadows_start: 0,
+            shadows_iter: self.shadows.iter().peekable(),
+            quads: &self.quads,
+            quads_start: 0,
+            quads_iter: self.quads.iter().peekable(),
+            paths: &self.paths,
+            paths_start: 0,
+            paths_iter: self.paths.iter().peekable(),
+            underlines: &self.underlines,
+            underlines_start: 0,
+            underlines_iter: self.underlines.iter().peekable(),
+            monochrome_sprites: &self.monochrome_sprites,
+            monochrome_sprites_start: 0,
+            monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(),
+            polychrome_sprites: &self.polychrome_sprites,
+            polychrome_sprites_start: 0,
+            polychrome_sprites_iter: self.polychrome_sprites.iter().peekable(),
+            surfaces: &self.surfaces,
+            surfaces_start: 0,
+            surfaces_iter: self.surfaces.iter().peekable(),
+        }
     }
+}
 
-    pub fn take_event_handlers(&mut self) -> Vec<EventHandler> {
-        self.event_handlers
-            .sort_by(|a, b| a.order.cmp(&b.order).reverse());
-        std::mem::take(&mut self.event_handlers)
-    }
+struct BatchIterator<'a> {
+    shadows: &'a [Shadow],
+    shadows_start: usize,
+    shadows_iter: Peekable<slice::Iter<'a, Shadow>>,
+    quads: &'a [Quad],
+    quads_start: usize,
+    quads_iter: Peekable<slice::Iter<'a, Quad>>,
+    paths: &'a [Path<ScaledPixels>],
+    paths_start: usize,
+    paths_iter: Peekable<slice::Iter<'a, Path<ScaledPixels>>>,
+    underlines: &'a [Underline],
+    underlines_start: usize,
+    underlines_iter: Peekable<slice::Iter<'a, Underline>>,
+    monochrome_sprites: &'a [MonochromeSprite],
+    monochrome_sprites_start: usize,
+    monochrome_sprites_iter: Peekable<slice::Iter<'a, MonochromeSprite>>,
+    polychrome_sprites: &'a [PolychromeSprite],
+    polychrome_sprites_start: usize,
+    polychrome_sprites_iter: Peekable<slice::Iter<'a, PolychromeSprite>>,
+    surfaces: &'a [Surface],
+    surfaces_start: usize,
+    surfaces_iter: Peekable<slice::Iter<'a, Surface>>,
 }
 
-impl SceneBuilder {
-    pub fn new() -> Self {
-        let mut this = SceneBuilder {
-            stacking_contexts: Vec::new(),
-            active_stacking_context_stack: Vec::new(),
-            #[cfg(debug_assertions)]
-            mouse_region_ids: HashSet::default(),
-            event_handlers: Vec::new(),
+impl<'a> Iterator for BatchIterator<'a> {
+    type Item = PrimitiveBatch<'a>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let mut orders_and_kinds = [
+            (
+                self.shadows_iter.peek().map(|s| s.order),
+                PrimitiveKind::Shadow,
+            ),
+            (self.quads_iter.peek().map(|q| q.order), PrimitiveKind::Quad),
+            (self.paths_iter.peek().map(|q| q.order), PrimitiveKind::Path),
+            (
+                self.underlines_iter.peek().map(|u| u.order),
+                PrimitiveKind::Underline,
+            ),
+            (
+                self.monochrome_sprites_iter.peek().map(|s| s.order),
+                PrimitiveKind::MonochromeSprite,
+            ),
+            (
+                self.polychrome_sprites_iter.peek().map(|s| s.order),
+                PrimitiveKind::PolychromeSprite,
+            ),
+            (
+                self.surfaces_iter.peek().map(|s| s.order),
+                PrimitiveKind::Surface,
+            ),
+        ];
+        orders_and_kinds.sort_by_key(|(order, kind)| (order.unwrap_or(u32::MAX), *kind));
+
+        let first = orders_and_kinds[0];
+        let second = orders_and_kinds[1];
+        let (batch_kind, max_order) = if first.0.is_some() {
+            (first.1, second.0.unwrap_or(u32::MAX))
+        } else {
+            return None;
         };
-        this.clear();
-        this
-    }
-
-    pub fn clear(&mut self) {
-        self.stacking_contexts.clear();
-        self.stacking_contexts.push(StackingContext::new(None, 0));
-        self.active_stacking_context_stack.clear();
-        self.active_stacking_context_stack.push(0);
-        #[cfg(debug_assertions)]
-        self.mouse_region_ids.clear();
-    }
-
-    pub fn build(&mut self, scale_factor: f32) -> Scene {
-        let mut stacking_contexts = std::mem::take(&mut self.stacking_contexts);
-        stacking_contexts.sort_by_key(|context| context.z_index);
-        let event_handlers = std::mem::take(&mut self.event_handlers);
-        self.clear();
 
-        Scene {
-            scale_factor,
-            stacking_contexts,
-            event_handlers,
+        match batch_kind {
+            PrimitiveKind::Shadow => {
+                let shadows_start = self.shadows_start;
+                let mut shadows_end = shadows_start + 1;
+                self.shadows_iter.next();
+                while self
+                    .shadows_iter
+                    .next_if(|shadow| shadow.order < max_order)
+                    .is_some()
+                {
+                    shadows_end += 1;
+                }
+                self.shadows_start = shadows_end;
+                Some(PrimitiveBatch::Shadows(
+                    &self.shadows[shadows_start..shadows_end],
+                ))
+            }
+            PrimitiveKind::Quad => {
+                let quads_start = self.quads_start;
+                let mut quads_end = quads_start + 1;
+                self.quads_iter.next();
+                while self
+                    .quads_iter
+                    .next_if(|quad| quad.order < max_order)
+                    .is_some()
+                {
+                    quads_end += 1;
+                }
+                self.quads_start = quads_end;
+                Some(PrimitiveBatch::Quads(&self.quads[quads_start..quads_end]))
+            }
+            PrimitiveKind::Path => {
+                let paths_start = self.paths_start;
+                let mut paths_end = paths_start + 1;
+                self.paths_iter.next();
+                while self
+                    .paths_iter
+                    .next_if(|path| path.order < max_order)
+                    .is_some()
+                {
+                    paths_end += 1;
+                }
+                self.paths_start = paths_end;
+                Some(PrimitiveBatch::Paths(&self.paths[paths_start..paths_end]))
+            }
+            PrimitiveKind::Underline => {
+                let underlines_start = self.underlines_start;
+                let mut underlines_end = underlines_start + 1;
+                self.underlines_iter.next();
+                while self
+                    .underlines_iter
+                    .next_if(|underline| underline.order < max_order)
+                    .is_some()
+                {
+                    underlines_end += 1;
+                }
+                self.underlines_start = underlines_end;
+                Some(PrimitiveBatch::Underlines(
+                    &self.underlines[underlines_start..underlines_end],
+                ))
+            }
+            PrimitiveKind::MonochromeSprite => {
+                let texture_id = self.monochrome_sprites_iter.peek().unwrap().tile.texture_id;
+                let sprites_start = self.monochrome_sprites_start;
+                let mut sprites_end = sprites_start + 1;
+                self.monochrome_sprites_iter.next();
+                while self
+                    .monochrome_sprites_iter
+                    .next_if(|sprite| {
+                        sprite.order < max_order && sprite.tile.texture_id == texture_id
+                    })
+                    .is_some()
+                {
+                    sprites_end += 1;
+                }
+                self.monochrome_sprites_start = sprites_end;
+                Some(PrimitiveBatch::MonochromeSprites {
+                    texture_id,
+                    sprites: &self.monochrome_sprites[sprites_start..sprites_end],
+                })
+            }
+            PrimitiveKind::PolychromeSprite => {
+                let texture_id = self.polychrome_sprites_iter.peek().unwrap().tile.texture_id;
+                let sprites_start = self.polychrome_sprites_start;
+                let mut sprites_end = self.polychrome_sprites_start + 1;
+                self.polychrome_sprites_iter.next();
+                while self
+                    .polychrome_sprites_iter
+                    .next_if(|sprite| {
+                        sprite.order < max_order && sprite.tile.texture_id == texture_id
+                    })
+                    .is_some()
+                {
+                    sprites_end += 1;
+                }
+                self.polychrome_sprites_start = sprites_end;
+                Some(PrimitiveBatch::PolychromeSprites {
+                    texture_id,
+                    sprites: &self.polychrome_sprites[sprites_start..sprites_end],
+                })
+            }
+            PrimitiveKind::Surface => {
+                let surfaces_start = self.surfaces_start;
+                let mut surfaces_end = surfaces_start + 1;
+                self.surfaces_iter.next();
+                while self
+                    .surfaces_iter
+                    .next_if(|surface| surface.order < max_order)
+                    .is_some()
+                {
+                    surfaces_end += 1;
+                }
+                self.surfaces_start = surfaces_end;
+                Some(PrimitiveBatch::Surfaces(
+                    &self.surfaces[surfaces_start..surfaces_end],
+                ))
+            }
         }
     }
+}
 
-    pub fn push_stacking_context(&mut self, clip_bounds: Option<RectF>, z_index: Option<usize>) {
-        let z_index = z_index.unwrap_or_else(|| self.active_stacking_context().z_index + 1);
-        self.active_stacking_context_stack
-            .push(self.stacking_contexts.len());
-        self.stacking_contexts
-            .push(StackingContext::new(clip_bounds, z_index))
-    }
-
-    pub fn pop_stacking_context(&mut self) {
-        self.active_stacking_context_stack.pop();
-        assert!(!self.active_stacking_context_stack.is_empty());
-    }
-
-    pub fn push_layer(&mut self, clip_bounds: Option<RectF>) {
-        self.active_stacking_context().push_layer(clip_bounds);
-    }
-
-    pub fn pop_layer(&mut self) {
-        self.active_stacking_context().pop_layer();
-    }
+#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
+pub enum PrimitiveKind {
+    Shadow,
+    #[default]
+    Quad,
+    Path,
+    Underline,
+    MonochromeSprite,
+    PolychromeSprite,
+    Surface,
+}
 
-    pub fn push_quad(&mut self, quad: Quad) {
-        self.active_layer().push_quad(quad)
-    }
+pub enum Primitive {
+    Shadow(Shadow),
+    Quad(Quad),
+    Path(Path<ScaledPixels>),
+    Underline(Underline),
+    MonochromeSprite(MonochromeSprite),
+    PolychromeSprite(PolychromeSprite),
+    Surface(Surface),
+}
 
-    pub fn push_cursor_region(&mut self, region: CursorRegion) {
-        if can_draw(region.bounds) {
-            self.active_layer().push_cursor_region(region);
+impl Primitive {
+    pub fn bounds(&self) -> &Bounds<ScaledPixels> {
+        match self {
+            Primitive::Shadow(shadow) => &shadow.bounds,
+            Primitive::Quad(quad) => &quad.bounds,
+            Primitive::Path(path) => &path.bounds,
+            Primitive::Underline(underline) => &underline.bounds,
+            Primitive::MonochromeSprite(sprite) => &sprite.bounds,
+            Primitive::PolychromeSprite(sprite) => &sprite.bounds,
+            Primitive::Surface(surface) => &surface.bounds,
         }
     }
 
-    pub fn push_mouse_region(&mut self, region: MouseRegion) {
-        if can_draw(region.bounds) {
-            // Ensure that Regions cannot be added to a scene with the same region id.
-            #[cfg(debug_assertions)]
-            let region_id;
-            #[cfg(debug_assertions)]
-            {
-                region_id = region.id();
-            }
-
-            if self.active_layer().push_mouse_region(region) {
-                #[cfg(debug_assertions)]
-                {
-                    if !self.mouse_region_ids.insert(region_id) {
-                        let tag_name = region_id.tag_type_name();
-                        panic!("Same MouseRegionId: {region_id:?} inserted multiple times to the same scene. \
-                            Will cause problems! Look for MouseRegion that uses Tag: {tag_name}");
-                    }
-                }
-            }
+    pub fn content_mask(&self) -> &ContentMask<ScaledPixels> {
+        match self {
+            Primitive::Shadow(shadow) => &shadow.content_mask,
+            Primitive::Quad(quad) => &quad.content_mask,
+            Primitive::Path(path) => &path.content_mask,
+            Primitive::Underline(underline) => &underline.content_mask,
+            Primitive::MonochromeSprite(sprite) => &sprite.content_mask,
+            Primitive::PolychromeSprite(sprite) => &sprite.content_mask,
+            Primitive::Surface(surface) => &surface.content_mask,
         }
     }
+}
 
-    pub fn push_image(&mut self, image: Image) {
-        self.active_layer().push_image(image)
-    }
+#[derive(Debug)]
+pub(crate) enum PrimitiveBatch<'a> {
+    Shadows(&'a [Shadow]),
+    Quads(&'a [Quad]),
+    Paths(&'a [Path<ScaledPixels>]),
+    Underlines(&'a [Underline]),
+    MonochromeSprites {
+        texture_id: AtlasTextureId,
+        sprites: &'a [MonochromeSprite],
+    },
+    PolychromeSprites {
+        texture_id: AtlasTextureId,
+        sprites: &'a [PolychromeSprite],
+    },
+    Surfaces(&'a [Surface]),
+}
 
-    pub fn push_surface(&mut self, surface: Surface) {
-        self.active_layer().push_surface(surface)
-    }
+#[derive(Default, Debug, Clone, Eq, PartialEq)]
+#[repr(C)]
+pub struct Quad {
+    pub order: u32, // Initially a LayerId, then a DrawOrder.
+    pub bounds: Bounds<ScaledPixels>,
+    pub content_mask: ContentMask<ScaledPixels>,
+    pub background: Hsla,
+    pub border_color: Hsla,
+    pub corner_radii: Corners<ScaledPixels>,
+    pub border_widths: Edges<ScaledPixels>,
+}
 
-    pub fn push_underline(&mut self, underline: Underline) {
-        self.active_layer().push_underline(underline)
+impl Ord for Quad {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.order.cmp(&other.order)
     }
+}
 
-    pub fn push_shadow(&mut self, shadow: Shadow) {
-        self.active_layer().push_shadow(shadow)
+impl PartialOrd for Quad {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        Some(self.cmp(other))
     }
+}
 
-    pub fn push_glyph(&mut self, glyph: Glyph) {
-        self.active_layer().push_glyph(glyph)
+impl From<Quad> for Primitive {
+    fn from(quad: Quad) -> Self {
+        Primitive::Quad(quad)
     }
+}
 
-    pub fn push_image_glyph(&mut self, image_glyph: ImageGlyph) {
-        self.active_layer().push_image_glyph(image_glyph)
-    }
+#[derive(Debug, Clone, Eq, PartialEq)]
+#[repr(C)]
+pub struct Underline {
+    pub order: u32,
+    pub bounds: Bounds<ScaledPixels>,
+    pub content_mask: ContentMask<ScaledPixels>,
+    pub thickness: ScaledPixels,
+    pub color: Hsla,
+    pub wavy: bool,
+}
 
-    pub fn push_icon(&mut self, icon: Icon) {
-        self.active_layer().push_icon(icon)
+impl Ord for Underline {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.order.cmp(&other.order)
     }
+}
 
-    pub fn push_path(&mut self, path: Path) {
-        self.active_layer().push_path(path);
+impl PartialOrd for Underline {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        Some(self.cmp(other))
     }
+}
 
-    fn active_stacking_context(&mut self) -> &mut StackingContext {
-        let ix = *self.active_stacking_context_stack.last().unwrap();
-        &mut self.stacking_contexts[ix]
+impl From<Underline> for Primitive {
+    fn from(underline: Underline) -> Self {
+        Primitive::Underline(underline)
     }
+}
 
-    fn active_layer(&mut self) -> &mut Layer {
-        self.active_stacking_context().active_layer()
-    }
+#[derive(Debug, Clone, Eq, PartialEq)]
+#[repr(C)]
+pub struct Shadow {
+    pub order: u32,
+    pub bounds: Bounds<ScaledPixels>,
+    pub corner_radii: Corners<ScaledPixels>,
+    pub content_mask: ContentMask<ScaledPixels>,
+    pub color: Hsla,
+    pub blur_radius: ScaledPixels,
 }
 
-impl StackingContext {
-    fn new(clip_bounds: Option<RectF>, z_index: usize) -> Self {
-        Self {
-            layers: vec![Layer::new(clip_bounds)],
-            active_layer_stack: vec![0],
-            z_index,
-        }
+impl Ord for Shadow {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.order.cmp(&other.order)
     }
+}
 
-    fn active_layer(&mut self) -> &mut Layer {
-        &mut self.layers[*self.active_layer_stack.last().unwrap()]
+impl PartialOrd for Shadow {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        Some(self.cmp(other))
     }
+}
 
-    fn push_layer(&mut self, clip_bounds: Option<RectF>) {
-        let parent_clip_bounds = self.active_layer().clip_bounds();
-        let clip_bounds = clip_bounds
-            .map(|clip_bounds| {
-                clip_bounds
-                    .intersection(parent_clip_bounds.unwrap_or(clip_bounds))
-                    .unwrap_or_else(|| {
-                        if !clip_bounds.is_empty() {
-                            log::warn!("specified clip bounds are disjoint from parent layer");
-                        }
-                        RectF::default()
-                    })
-            })
-            .or(parent_clip_bounds);
-
-        let ix = self.layers.len();
-        self.layers.push(Layer::new(clip_bounds));
-        self.active_layer_stack.push(ix);
+impl From<Shadow> for Primitive {
+    fn from(shadow: Shadow) -> Self {
+        Primitive::Shadow(shadow)
     }
+}
 
-    fn pop_layer(&mut self) {
-        self.active_layer_stack.pop().unwrap();
-        assert!(!self.active_layer_stack.is_empty());
-    }
+#[derive(Clone, Debug, Eq, PartialEq)]
+#[repr(C)]
+pub struct MonochromeSprite {
+    pub order: u32,
+    pub bounds: Bounds<ScaledPixels>,
+    pub content_mask: ContentMask<ScaledPixels>,
+    pub color: Hsla,
+    pub tile: AtlasTile,
 }
 
-impl Layer {
-    pub fn new(clip_bounds: Option<RectF>) -> Self {
-        Self {
-            clip_bounds,
-            quads: Default::default(),
-            underlines: Default::default(),
-            images: Default::default(),
-            surfaces: Default::default(),
-            shadows: Default::default(),
-            image_glyphs: Default::default(),
-            glyphs: Default::default(),
-            icons: Default::default(),
-            paths: Default::default(),
-            cursor_regions: Default::default(),
-            mouse_regions: Default::default(),
+impl Ord for MonochromeSprite {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        match self.order.cmp(&other.order) {
+            std::cmp::Ordering::Equal => self.tile.tile_id.cmp(&other.tile.tile_id),
+            order => order,
         }
     }
+}
 
-    pub fn clip_bounds(&self) -> Option<RectF> {
-        self.clip_bounds
+impl PartialOrd for MonochromeSprite {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        Some(self.cmp(other))
     }
+}
 
-    fn push_quad(&mut self, quad: Quad) {
-        if can_draw(quad.bounds) {
-            self.quads.push(quad);
-        }
+impl From<MonochromeSprite> for Primitive {
+    fn from(sprite: MonochromeSprite) -> Self {
+        Primitive::MonochromeSprite(sprite)
     }
+}
 
-    pub fn quads(&self) -> &[Quad] {
-        self.quads.as_slice()
-    }
+#[derive(Clone, Debug, Eq, PartialEq)]
+#[repr(C)]
+pub struct PolychromeSprite {
+    pub order: u32,
+    pub bounds: Bounds<ScaledPixels>,
+    pub content_mask: ContentMask<ScaledPixels>,
+    pub corner_radii: Corners<ScaledPixels>,
+    pub tile: AtlasTile,
+    pub grayscale: bool,
+}
 
-    fn push_cursor_region(&mut self, region: CursorRegion) {
-        if let Some(bounds) = region
-            .bounds
-            .intersection(self.clip_bounds.unwrap_or(region.bounds))
-        {
-            if can_draw(bounds) {
-                self.cursor_regions.push(region);
-            }
+impl Ord for PolychromeSprite {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        match self.order.cmp(&other.order) {
+            std::cmp::Ordering::Equal => self.tile.tile_id.cmp(&other.tile.tile_id),
+            order => order,
         }
     }
+}
 
-    fn push_mouse_region(&mut self, region: MouseRegion) -> bool {
-        if let Some(bounds) = region
-            .bounds
-            .intersection(self.clip_bounds.unwrap_or(region.bounds))
-        {
-            if can_draw(bounds) {
-                self.mouse_regions.push(region);
-                return true;
-            }
-        }
-        false
+impl PartialOrd for PolychromeSprite {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        Some(self.cmp(other))
     }
+}
 
-    fn push_underline(&mut self, underline: Underline) {
-        if underline.width > 0. {
-            self.underlines.push(underline);
-        }
+impl From<PolychromeSprite> for Primitive {
+    fn from(sprite: PolychromeSprite) -> Self {
+        Primitive::PolychromeSprite(sprite)
     }
+}
 
-    pub fn underlines(&self) -> &[Underline] {
-        self.underlines.as_slice()
-    }
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Surface {
+    pub order: u32,
+    pub bounds: Bounds<ScaledPixels>,
+    pub content_mask: ContentMask<ScaledPixels>,
+    pub image_buffer: media::core_video::CVImageBuffer,
+}
 
-    fn push_image(&mut self, image: Image) {
-        if can_draw(image.bounds) {
-            self.images.push(image);
-        }
+impl Ord for Surface {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.order.cmp(&other.order)
     }
+}
 
-    pub fn images(&self) -> &[Image] {
-        self.images.as_slice()
+impl PartialOrd for Surface {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        Some(self.cmp(other))
     }
+}
 
-    fn push_surface(&mut self, surface: Surface) {
-        if can_draw(surface.bounds) {
-            self.surfaces.push(surface);
-        }
+impl From<Surface> for Primitive {
+    fn from(surface: Surface) -> Self {
+        Primitive::Surface(surface)
     }
+}
 
-    pub fn surfaces(&self) -> &[Surface] {
-        self.surfaces.as_slice()
-    }
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub(crate) struct PathId(pub(crate) usize);
+
+#[derive(Debug)]
+pub struct Path<P: Clone + Default + Debug> {
+    pub(crate) id: PathId,
+    order: u32,
+    pub(crate) bounds: Bounds<P>,
+    pub(crate) content_mask: ContentMask<P>,
+    pub(crate) vertices: Vec<PathVertex<P>>,
+    pub(crate) color: Hsla,
+    start: Point<P>,
+    current: Point<P>,
+    contour_count: usize,
+}
 
-    fn push_shadow(&mut self, shadow: Shadow) {
-        if can_draw(shadow.bounds) {
-            self.shadows.push(shadow);
+impl Path<Pixels> {
+    pub fn new(start: Point<Pixels>) -> Self {
+        Self {
+            id: PathId(0),
+            order: 0,
+            vertices: Vec::new(),
+            start,
+            current: start,
+            bounds: Bounds {
+                origin: start,
+                size: Default::default(),
+            },
+            content_mask: Default::default(),
+            color: Default::default(),
+            contour_count: 0,
         }
     }
 
-    pub fn shadows(&self) -> &[Shadow] {
-        self.shadows.as_slice()
+    pub fn scale(&self, factor: f32) -> Path<ScaledPixels> {
+        Path {
+            id: self.id,
+            order: self.order,
+            bounds: self.bounds.scale(factor),
+            content_mask: self.content_mask.scale(factor),
+            vertices: self
+                .vertices
+                .iter()
+                .map(|vertex| vertex.scale(factor))
+                .collect(),
+            start: self.start.map(|start| start.scale(factor)),
+            current: self.current.scale(factor),
+            contour_count: self.contour_count,
+            color: self.color,
+        }
     }
 
-    fn push_image_glyph(&mut self, glyph: ImageGlyph) {
-        self.image_glyphs.push(glyph);
+    pub fn line_to(&mut self, to: Point<Pixels>) {
+        self.contour_count += 1;
+        if self.contour_count > 1 {
+            self.push_triangle(
+                (self.start, self.current, to),
+                (point(0., 1.), point(0., 1.), point(0., 1.)),
+            );
+        }
+        self.current = to;
     }
 
-    pub fn image_glyphs(&self) -> &[ImageGlyph] {
-        self.image_glyphs.as_slice()
-    }
+    pub fn curve_to(&mut self, to: Point<Pixels>, ctrl: Point<Pixels>) {
+        self.contour_count += 1;
+        if self.contour_count > 1 {
+            self.push_triangle(
+                (self.start, self.current, to),
+                (point(0., 1.), point(0., 1.), point(0., 1.)),
+            );
+        }
 
-    fn push_glyph(&mut self, glyph: Glyph) {
-        self.glyphs.push(glyph);
+        self.push_triangle(
+            (self.current, ctrl, to),
+            (point(0., 0.), point(0.5, 0.), point(1., 1.)),
+        );
+        self.current = to;
     }
 
-    pub fn glyphs(&self) -> &[Glyph] {
-        self.glyphs.as_slice()
+    fn push_triangle(
+        &mut self,
+        xy: (Point<Pixels>, Point<Pixels>, Point<Pixels>),
+        st: (Point<f32>, Point<f32>, Point<f32>),
+    ) {
+        self.bounds = self
+            .bounds
+            .union(&Bounds {
+                origin: xy.0,
+                size: Default::default(),
+            })
+            .union(&Bounds {
+                origin: xy.1,
+                size: Default::default(),
+            })
+            .union(&Bounds {
+                origin: xy.2,
+                size: Default::default(),
+            });
+
+        self.vertices.push(PathVertex {
+            xy_position: xy.0,
+            st_position: st.0,
+            content_mask: Default::default(),
+        });
+        self.vertices.push(PathVertex {
+            xy_position: xy.1,
+            st_position: st.1,
+            content_mask: Default::default(),
+        });
+        self.vertices.push(PathVertex {
+            xy_position: xy.2,
+            st_position: st.2,
+            content_mask: Default::default(),
+        });
     }
+}
 
-    pub fn push_icon(&mut self, icon: Icon) {
-        if can_draw(icon.bounds) {
-            self.icons.push(icon);
-        }
-    }
+impl Eq for Path<ScaledPixels> {}
 
-    pub fn icons(&self) -> &[Icon] {
-        self.icons.as_slice()
+impl PartialEq for Path<ScaledPixels> {
+    fn eq(&self, other: &Self) -> bool {
+        self.order == other.order
     }
+}
 
-    fn push_path(&mut self, path: Path) {
-        if can_draw(path.bounds) {
-            self.paths.push(path);
-        }
+impl Ord for Path<ScaledPixels> {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.order.cmp(&other.order)
     }
+}
 
-    pub fn paths(&self) -> &[Path] {
-        self.paths.as_slice()
+impl PartialOrd for Path<ScaledPixels> {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        Some(self.cmp(other))
     }
 }
 
-impl MouseRegion {
-    pub fn id(&self) -> MouseRegionId {
-        self.id
+impl From<Path<ScaledPixels>> for Primitive {
+    fn from(path: Path<ScaledPixels>) -> Self {
+        Primitive::Path(path)
     }
 }
 
-pub struct EventHandler {
-    pub order: u32,
-    // The &dyn Any parameter below expects an event.
-    pub handler: Rc<dyn Fn(&dyn Any, &mut WindowContext) -> bool>,
-    pub event_type: TypeId,
+#[derive(Clone, Debug)]
+#[repr(C)]
+pub struct PathVertex<P: Clone + Default + Debug> {
+    pub(crate) xy_position: Point<P>,
+    pub(crate) st_position: Point<f32>,
+    pub(crate) content_mask: ContentMask<P>,
 }
 
-fn can_draw(bounds: RectF) -> bool {
-    let size = bounds.size();
-    size.x() > 0. && size.y() > 0.
+impl PathVertex<Pixels> {
+    pub fn scale(&self, factor: f32) -> PathVertex<ScaledPixels> {
+        PathVertex {
+            xy_position: self.xy_position.scale(factor),
+            st_position: self.st_position,
+            content_mask: self.content_mask.scale(factor),
+        }
+    }
 }
+
+#[derive(Copy, Clone, Debug)]
+pub struct AtlasId(pub(crate) usize);

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

@@ -1,270 +0,0 @@
-use crate::{
-    platform::{MouseButtonEvent, MouseMovedEvent, ScrollWheelEvent},
-    scene::mouse_region::HandlerKey,
-};
-use pathfinder_geometry::{rect::RectF, vector::Vector2F};
-use std::{
-    mem::{discriminant, Discriminant},
-    ops::Deref,
-};
-
-#[derive(Debug, Default, Clone)]
-pub struct MouseMove {
-    pub region: RectF,
-    pub platform_event: MouseMovedEvent,
-}
-
-impl Deref for MouseMove {
-    type Target = MouseMovedEvent;
-
-    fn deref(&self) -> &Self::Target {
-        &self.platform_event
-    }
-}
-
-#[derive(Debug, Default, Clone)]
-pub struct MouseMoveOut {
-    pub region: RectF,
-}
-
-#[derive(Debug, Default, Clone)]
-pub struct MouseDrag {
-    pub region: RectF,
-    pub prev_mouse_position: Vector2F,
-    pub platform_event: MouseMovedEvent,
-    pub end: bool,
-}
-
-impl Deref for MouseDrag {
-    type Target = MouseMovedEvent;
-
-    fn deref(&self) -> &Self::Target {
-        &self.platform_event
-    }
-}
-
-#[derive(Debug, Default, Clone)]
-pub struct MouseHover {
-    pub region: RectF,
-    pub started: bool,
-    pub platform_event: MouseMovedEvent,
-}
-
-impl Deref for MouseHover {
-    type Target = MouseMovedEvent;
-
-    fn deref(&self) -> &Self::Target {
-        &self.platform_event
-    }
-}
-
-#[derive(Debug, Default, Clone)]
-pub struct MouseDown {
-    pub region: RectF,
-    pub platform_event: MouseButtonEvent,
-}
-
-impl Deref for MouseDown {
-    type Target = MouseButtonEvent;
-
-    fn deref(&self) -> &Self::Target {
-        &self.platform_event
-    }
-}
-
-#[derive(Debug, Default, Clone)]
-pub struct MouseUp {
-    pub region: RectF,
-    pub platform_event: MouseButtonEvent,
-}
-
-impl Deref for MouseUp {
-    type Target = MouseButtonEvent;
-
-    fn deref(&self) -> &Self::Target {
-        &self.platform_event
-    }
-}
-
-#[derive(Debug, Default, Clone)]
-pub struct MouseClick {
-    pub region: RectF,
-    pub platform_event: MouseButtonEvent,
-}
-
-impl Deref for MouseClick {
-    type Target = MouseButtonEvent;
-
-    fn deref(&self) -> &Self::Target {
-        &self.platform_event
-    }
-}
-
-#[derive(Debug, Default, Clone)]
-pub struct MouseClickOut {
-    pub region: RectF,
-    pub platform_event: MouseButtonEvent,
-}
-
-impl Deref for MouseClickOut {
-    type Target = MouseButtonEvent;
-
-    fn deref(&self) -> &Self::Target {
-        &self.platform_event
-    }
-}
-
-#[derive(Debug, Default, Clone)]
-pub struct MouseDownOut {
-    pub region: RectF,
-    pub platform_event: MouseButtonEvent,
-}
-
-impl Deref for MouseDownOut {
-    type Target = MouseButtonEvent;
-
-    fn deref(&self) -> &Self::Target {
-        &self.platform_event
-    }
-}
-
-#[derive(Debug, Default, Clone)]
-pub struct MouseUpOut {
-    pub region: RectF,
-    pub platform_event: MouseButtonEvent,
-}
-
-impl Deref for MouseUpOut {
-    type Target = MouseButtonEvent;
-
-    fn deref(&self) -> &Self::Target {
-        &self.platform_event
-    }
-}
-
-#[derive(Debug, Default, Clone)]
-pub struct MouseScrollWheel {
-    pub region: RectF,
-    pub platform_event: ScrollWheelEvent,
-}
-
-impl Deref for MouseScrollWheel {
-    type Target = ScrollWheelEvent;
-
-    fn deref(&self) -> &Self::Target {
-        &self.platform_event
-    }
-}
-
-#[derive(Debug, Clone)]
-pub enum MouseEvent {
-    Move(MouseMove),
-    MoveOut(MouseMoveOut),
-    Drag(MouseDrag),
-    Hover(MouseHover),
-    Down(MouseDown),
-    Up(MouseUp),
-    Click(MouseClick),
-    ClickOut(MouseClickOut),
-    DownOut(MouseDownOut),
-    UpOut(MouseUpOut),
-    ScrollWheel(MouseScrollWheel),
-}
-
-impl MouseEvent {
-    pub fn set_region(&mut self, region: RectF) {
-        match self {
-            MouseEvent::Move(r) => r.region = region,
-            MouseEvent::MoveOut(r) => r.region = region,
-            MouseEvent::Drag(r) => r.region = region,
-            MouseEvent::Hover(r) => r.region = region,
-            MouseEvent::Down(r) => r.region = region,
-            MouseEvent::Up(r) => r.region = region,
-            MouseEvent::Click(r) => r.region = region,
-            MouseEvent::ClickOut(r) => r.region = region,
-            MouseEvent::DownOut(r) => r.region = region,
-            MouseEvent::UpOut(r) => r.region = region,
-            MouseEvent::ScrollWheel(r) => r.region = region,
-        }
-    }
-
-    /// When true, mouse event handlers must call cx.propagate_event() to bubble
-    /// the event to handlers they are painted on top of.
-    pub fn is_capturable(&self) -> bool {
-        match self {
-            MouseEvent::Move(_) => true,
-            MouseEvent::MoveOut(_) => false,
-            MouseEvent::Drag(_) => true,
-            MouseEvent::Hover(_) => false,
-            MouseEvent::Down(_) => true,
-            MouseEvent::Up(_) => true,
-            MouseEvent::Click(_) => true,
-            MouseEvent::ClickOut(_) => true,
-            MouseEvent::DownOut(_) => false,
-            MouseEvent::UpOut(_) => false,
-            MouseEvent::ScrollWheel(_) => true,
-        }
-    }
-}
-
-impl MouseEvent {
-    pub fn move_disc() -> Discriminant<MouseEvent> {
-        discriminant(&MouseEvent::Move(Default::default()))
-    }
-
-    pub fn move_out_disc() -> Discriminant<MouseEvent> {
-        discriminant(&MouseEvent::MoveOut(Default::default()))
-    }
-
-    pub fn drag_disc() -> Discriminant<MouseEvent> {
-        discriminant(&MouseEvent::Drag(Default::default()))
-    }
-
-    pub fn hover_disc() -> Discriminant<MouseEvent> {
-        discriminant(&MouseEvent::Hover(Default::default()))
-    }
-
-    pub fn down_disc() -> Discriminant<MouseEvent> {
-        discriminant(&MouseEvent::Down(Default::default()))
-    }
-
-    pub fn up_disc() -> Discriminant<MouseEvent> {
-        discriminant(&MouseEvent::Up(Default::default()))
-    }
-
-    pub fn up_out_disc() -> Discriminant<MouseEvent> {
-        discriminant(&MouseEvent::UpOut(Default::default()))
-    }
-
-    pub fn click_disc() -> Discriminant<MouseEvent> {
-        discriminant(&MouseEvent::Click(Default::default()))
-    }
-
-    pub fn click_out_disc() -> Discriminant<MouseEvent> {
-        discriminant(&MouseEvent::ClickOut(Default::default()))
-    }
-
-    pub fn down_out_disc() -> Discriminant<MouseEvent> {
-        discriminant(&MouseEvent::DownOut(Default::default()))
-    }
-
-    pub fn scroll_wheel_disc() -> Discriminant<MouseEvent> {
-        discriminant(&MouseEvent::ScrollWheel(Default::default()))
-    }
-
-    pub fn handler_key(&self) -> HandlerKey {
-        match self {
-            MouseEvent::Move(_) => HandlerKey::new(Self::move_disc(), None),
-            MouseEvent::MoveOut(_) => HandlerKey::new(Self::move_out_disc(), None),
-            MouseEvent::Drag(e) => HandlerKey::new(Self::drag_disc(), e.pressed_button),
-            MouseEvent::Hover(_) => HandlerKey::new(Self::hover_disc(), None),
-            MouseEvent::Down(e) => HandlerKey::new(Self::down_disc(), Some(e.button)),
-            MouseEvent::Up(e) => HandlerKey::new(Self::up_disc(), Some(e.button)),
-            MouseEvent::Click(e) => HandlerKey::new(Self::click_disc(), Some(e.button)),
-            MouseEvent::ClickOut(e) => HandlerKey::new(Self::click_out_disc(), Some(e.button)),
-            MouseEvent::UpOut(e) => HandlerKey::new(Self::up_out_disc(), Some(e.button)),
-            MouseEvent::DownOut(e) => HandlerKey::new(Self::down_out_disc(), Some(e.button)),
-            MouseEvent::ScrollWheel(_) => HandlerKey::new(Self::scroll_wheel_disc(), None),
-        }
-    }
-}

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

@@ -1,555 +0,0 @@
-use crate::{platform::MouseButton, window::WindowContext, EventContext, TypeTag, ViewContext};
-use collections::HashMap;
-use pathfinder_geometry::rect::RectF;
-use smallvec::SmallVec;
-use std::{any::Any, fmt::Debug, mem::Discriminant, rc::Rc};
-
-use super::{
-    mouse_event::{
-        MouseClick, MouseDown, MouseDownOut, MouseDrag, MouseEvent, MouseHover, MouseMove, MouseUp,
-        MouseUpOut,
-    },
-    MouseClickOut, MouseMoveOut, MouseScrollWheel,
-};
-
-#[derive(Clone)]
-pub struct MouseRegion {
-    pub id: MouseRegionId,
-    pub bounds: RectF,
-    pub handlers: HandlerSet,
-    pub hoverable: bool,
-    pub notify_on_hover: bool,
-    pub notify_on_click: bool,
-}
-
-impl MouseRegion {
-    /// Region ID is used to track semantically equivalent mouse regions across render passes.
-    /// e.g. if you have mouse handlers attached to a list item type, then each item of the list
-    /// should pass a different (consistent) region_id. If you have one big region that covers your
-    /// whole component, just pass the view_id again.
-    pub fn new<Tag: 'static>(view_id: usize, region_id: usize, bounds: RectF) -> Self {
-        Self::from_handlers(
-            TypeTag::new::<Tag>(),
-            view_id,
-            region_id,
-            bounds,
-            Default::default(),
-        )
-    }
-
-    pub fn handle_all<Tag: 'static>(view_id: usize, region_id: usize, bounds: RectF) -> Self {
-        Self::from_handlers(
-            TypeTag::new::<Tag>(),
-            view_id,
-            region_id,
-            bounds,
-            HandlerSet::capture_all(),
-        )
-    }
-
-    pub fn from_handlers(
-        tag: TypeTag,
-        view_id: usize,
-        region_id: usize,
-        bounds: RectF,
-        handlers: HandlerSet,
-    ) -> Self {
-        Self {
-            id: MouseRegionId {
-                view_id,
-                tag,
-                region_id,
-            },
-            bounds,
-            handlers,
-            hoverable: true,
-            notify_on_hover: false,
-            notify_on_click: false,
-        }
-    }
-
-    pub fn on_down<V, F>(mut self, button: MouseButton, handler: F) -> Self
-    where
-        V: 'static,
-        F: Fn(MouseDown, &mut V, &mut EventContext<V>) + 'static,
-    {
-        self.handlers = self.handlers.on_down(button, handler);
-        self
-    }
-
-    pub fn on_up<V, F>(mut self, button: MouseButton, handler: F) -> Self
-    where
-        V: 'static,
-        F: Fn(MouseUp, &mut V, &mut EventContext<V>) + 'static,
-    {
-        self.handlers = self.handlers.on_up(button, handler);
-        self
-    }
-
-    pub fn on_click<V, F>(mut self, button: MouseButton, handler: F) -> Self
-    where
-        V: 'static,
-        F: Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
-    {
-        self.handlers = self.handlers.on_click(button, handler);
-        self
-    }
-
-    pub fn on_click_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
-    where
-        V: 'static,
-        F: Fn(MouseClickOut, &mut V, &mut EventContext<V>) + 'static,
-    {
-        self.handlers = self.handlers.on_click_out(button, handler);
-        self
-    }
-
-    pub fn on_down_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
-    where
-        V: 'static,
-        F: Fn(MouseDownOut, &mut V, &mut EventContext<V>) + 'static,
-    {
-        self.handlers = self.handlers.on_down_out(button, handler);
-        self
-    }
-
-    pub fn on_up_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
-    where
-        V: 'static,
-        F: Fn(MouseUpOut, &mut V, &mut EventContext<V>) + 'static,
-    {
-        self.handlers = self.handlers.on_up_out(button, handler);
-        self
-    }
-
-    pub fn on_drag<V, F>(mut self, button: MouseButton, handler: F) -> Self
-    where
-        V: 'static,
-        F: Fn(MouseDrag, &mut V, &mut EventContext<V>) + 'static,
-    {
-        self.handlers = self.handlers.on_drag(button, handler);
-        self
-    }
-
-    pub fn on_hover<V, F>(mut self, handler: F) -> Self
-    where
-        V: 'static,
-        F: Fn(MouseHover, &mut V, &mut EventContext<V>) + 'static,
-    {
-        self.handlers = self.handlers.on_hover(handler);
-        self
-    }
-
-    pub fn on_move<V, F>(mut self, handler: F) -> Self
-    where
-        V: 'static,
-        F: Fn(MouseMove, &mut V, &mut EventContext<V>) + 'static,
-    {
-        self.handlers = self.handlers.on_move(handler);
-        self
-    }
-
-    pub fn on_move_out<V, F>(mut self, handler: F) -> Self
-    where
-        V: 'static,
-        F: Fn(MouseMoveOut, &mut V, &mut EventContext<V>) + 'static,
-    {
-        self.handlers = self.handlers.on_move_out(handler);
-        self
-    }
-
-    pub fn on_scroll<V, F>(mut self, handler: F) -> Self
-    where
-        V: 'static,
-        F: Fn(MouseScrollWheel, &mut V, &mut EventContext<V>) + 'static,
-    {
-        self.handlers = self.handlers.on_scroll(handler);
-        self
-    }
-
-    pub fn with_hoverable(mut self, is_hoverable: bool) -> Self {
-        self.hoverable = is_hoverable;
-        self
-    }
-
-    pub fn with_notify_on_hover(mut self, notify: bool) -> Self {
-        self.notify_on_hover = notify;
-        self
-    }
-
-    pub fn with_notify_on_click(mut self, notify: bool) -> Self {
-        self.notify_on_click = notify;
-        self
-    }
-}
-
-#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, PartialOrd, Ord)]
-pub struct MouseRegionId {
-    view_id: usize,
-    tag: TypeTag,
-    region_id: usize,
-}
-
-impl MouseRegionId {
-    pub(crate) fn new(tag: TypeTag, view_id: usize, region_id: usize) -> Self {
-        MouseRegionId {
-            view_id,
-            region_id,
-            tag,
-        }
-    }
-
-    pub fn view_id(&self) -> usize {
-        self.view_id
-    }
-
-    #[cfg(debug_assertions)]
-    pub fn tag_type_name(&self) -> &'static str {
-        self.tag.type_name()
-    }
-}
-
-pub type HandlerCallback = Rc<dyn Fn(MouseEvent, &mut dyn Any, &mut WindowContext, usize) -> bool>;
-
-#[derive(Clone, PartialEq, Eq, Hash)]
-pub struct HandlerKey {
-    event_kind: Discriminant<MouseEvent>,
-    button: Option<MouseButton>,
-}
-
-impl HandlerKey {
-    pub fn new(event_kind: Discriminant<MouseEvent>, button: Option<MouseButton>) -> HandlerKey {
-        HandlerKey { event_kind, button }
-    }
-}
-
-#[derive(Clone, Default)]
-pub struct HandlerSet {
-    set: HashMap<HandlerKey, SmallVec<[HandlerCallback; 1]>>,
-}
-
-impl HandlerSet {
-    pub fn capture_all() -> Self {
-        let mut set: HashMap<HandlerKey, SmallVec<[HandlerCallback; 1]>> = HashMap::default();
-
-        set.insert(
-            HandlerKey::new(MouseEvent::move_disc(), None),
-            SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
-        );
-        set.insert(
-            HandlerKey::new(MouseEvent::hover_disc(), None),
-            SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
-        );
-        for button in MouseButton::all() {
-            set.insert(
-                HandlerKey::new(MouseEvent::drag_disc(), Some(button)),
-                SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
-            );
-            set.insert(
-                HandlerKey::new(MouseEvent::down_disc(), Some(button)),
-                SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
-            );
-            set.insert(
-                HandlerKey::new(MouseEvent::up_disc(), Some(button)),
-                SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
-            );
-            set.insert(
-                HandlerKey::new(MouseEvent::click_disc(), Some(button)),
-                SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
-            );
-            set.insert(
-                HandlerKey::new(MouseEvent::click_out_disc(), Some(button)),
-                SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
-            );
-            set.insert(
-                HandlerKey::new(MouseEvent::down_out_disc(), Some(button)),
-                SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
-            );
-            set.insert(
-                HandlerKey::new(MouseEvent::up_out_disc(), Some(button)),
-                SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
-            );
-        }
-        set.insert(
-            HandlerKey::new(MouseEvent::scroll_wheel_disc(), None),
-            SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
-        );
-
-        HandlerSet { set }
-    }
-
-    pub fn get(&self, key: &HandlerKey) -> Option<&[HandlerCallback]> {
-        self.set.get(key).map(|vec| vec.as_slice())
-    }
-
-    pub fn contains(
-        &self,
-        discriminant: Discriminant<MouseEvent>,
-        button: Option<MouseButton>,
-    ) -> bool {
-        self.set
-            .contains_key(&HandlerKey::new(discriminant, button))
-    }
-
-    fn insert(
-        &mut self,
-        event_kind: Discriminant<MouseEvent>,
-        button: Option<MouseButton>,
-        callback: HandlerCallback,
-    ) {
-        use std::collections::hash_map::Entry;
-
-        match self.set.entry(HandlerKey::new(event_kind, button)) {
-            Entry::Occupied(mut vec) => {
-                vec.get_mut().push(callback);
-            }
-
-            Entry::Vacant(entry) => {
-                entry.insert(SmallVec::from_buf([callback]));
-            }
-        }
-    }
-
-    pub fn on_move<V, F>(mut self, handler: F) -> Self
-    where
-        V: 'static,
-        F: Fn(MouseMove, &mut V, &mut EventContext<V>) + 'static,
-    {
-        self.insert(MouseEvent::move_disc(), None,
-            Rc::new(move |region_event, view, cx, view_id| {
-                if let MouseEvent::Move(e) = region_event {
-                    let view = view.downcast_mut().unwrap();
-                    let mut cx = ViewContext::mutable(cx, view_id);
-                    let mut cx = EventContext::new(&mut cx);
-                    handler(e, view, &mut cx);
-                    cx.handled
-                } else {
-                    panic!(
-                        "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Move, found {:?}",
-                        region_event);
-                }
-            }));
-        self
-    }
-
-    pub fn on_move_out<V, F>(mut self, handler: F) -> Self
-    where
-        V: 'static,
-        F: Fn(MouseMoveOut, &mut V, &mut EventContext<V>) + 'static,
-    {
-        self.insert(MouseEvent::move_out_disc(), None,
-            Rc::new(move |region_event, view, cx, view_id| {
-                if let MouseEvent::MoveOut(e) = region_event {
-                    let view = view.downcast_mut().unwrap();
-                    let mut cx = ViewContext::<V>::mutable(cx, view_id);
-                    let mut cx = EventContext::new(&mut cx);
-                    handler(e, view, &mut cx);
-                    cx.handled
-                } else {
-                    panic!(
-                        "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::MoveOut, found {:?}",
-                        region_event);
-                }
-            }));
-        self
-    }
-
-    pub fn on_down<V, F>(mut self, button: MouseButton, handler: F) -> Self
-    where
-        V: 'static,
-        F: Fn(MouseDown, &mut V, &mut EventContext<V>) + 'static,
-    {
-        self.insert(MouseEvent::down_disc(), Some(button),
-            Rc::new(move |region_event, view, cx, view_id| {
-                if let MouseEvent::Down(e) = region_event {
-                    let view = view.downcast_mut().unwrap();
-                    let mut cx = ViewContext::mutable(cx, view_id);
-                    let mut cx = EventContext::new(&mut cx);
-                    handler(e, view, &mut cx);
-                    cx.handled
-                } else {
-                    panic!(
-                        "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Down, found {:?}",
-                        region_event);
-                }
-            }));
-        self
-    }
-
-    pub fn on_up<V, F>(mut self, button: MouseButton, handler: F) -> Self
-    where
-        V: 'static,
-        F: Fn(MouseUp, &mut V, &mut EventContext<V>) + 'static,
-    {
-        self.insert(MouseEvent::up_disc(), Some(button),
-            Rc::new(move |region_event, view, cx, view_id| {
-                if let MouseEvent::Up(e) = region_event {
-                    let view = view.downcast_mut().unwrap();
-                    let mut cx = ViewContext::mutable(cx, view_id);
-                    let mut cx = EventContext::new(&mut cx);
-                    handler(e, view, &mut cx);
-                    cx.handled
-                } else {
-                    panic!(
-                        "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Up, found {:?}",
-                        region_event);
-                }
-            }));
-        self
-    }
-
-    pub fn on_click<V, F>(mut self, button: MouseButton, handler: F) -> Self
-    where
-        V: 'static,
-        F: Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
-    {
-        self.insert(MouseEvent::click_disc(), Some(button),
-            Rc::new(move |region_event, view, cx, view_id| {
-                if let MouseEvent::Click(e) = region_event {
-                    let view = view.downcast_mut().unwrap();
-                    let mut cx = ViewContext::mutable(cx, view_id);
-                    let mut cx = EventContext::new(&mut cx);
-                    handler(e, view, &mut cx);
-                    cx.handled
-                } else {
-                    panic!(
-                        "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Click, found {:?}",
-                        region_event);
-                }
-            }));
-        self
-    }
-
-    pub fn on_click_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
-    where
-        V: 'static,
-        F: Fn(MouseClickOut, &mut V, &mut EventContext<V>) + 'static,
-    {
-        self.insert(MouseEvent::click_out_disc(), Some(button),
-            Rc::new(move |region_event, view, cx, view_id| {
-                if let MouseEvent::ClickOut(e) = region_event {
-                    let view = view.downcast_mut().unwrap();
-                    let mut cx = ViewContext::mutable(cx, view_id);
-                    let mut cx = EventContext::new(&mut cx);
-                    handler(e, view, &mut cx);
-                    cx.handled
-                } else {
-                    panic!(
-                        "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::ClickOut, found {:?}",
-                        region_event);
-                }
-            }));
-        self
-    }
-
-    pub fn on_down_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
-    where
-        V: 'static,
-        F: Fn(MouseDownOut, &mut V, &mut EventContext<V>) + 'static,
-    {
-        self.insert(MouseEvent::down_out_disc(), Some(button),
-            Rc::new(move |region_event, view, cx, view_id| {
-                if let MouseEvent::DownOut(e) = region_event {
-                    let view = view.downcast_mut().unwrap();
-                    let mut cx = ViewContext::mutable(cx, view_id);
-                    let mut cx = EventContext::new(&mut cx);
-                    handler(e, view, &mut cx);
-                    cx.handled
-                } else {
-                    panic!(
-                        "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::DownOut, found {:?}",
-                        region_event);
-                }
-            }));
-        self
-    }
-
-    pub fn on_up_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
-    where
-        V: 'static,
-        F: Fn(MouseUpOut, &mut V, &mut EventContext<V>) + 'static,
-    {
-        self.insert(MouseEvent::up_out_disc(), Some(button),
-            Rc::new(move |region_event, view, cx, view_id| {
-                if let MouseEvent::UpOut(e) = region_event {
-                    let view = view.downcast_mut().unwrap();
-                    let mut cx = ViewContext::mutable(cx, view_id);
-                    let mut cx = EventContext::new(&mut cx);
-                    handler(e, view, &mut cx);
-                    cx.handled
-                } else {
-                    panic!(
-                        "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::UpOut, found {:?}",
-                        region_event);
-                }
-            }));
-        self
-    }
-
-    pub fn on_drag<V, F>(mut self, button: MouseButton, handler: F) -> Self
-    where
-        V: 'static,
-        F: Fn(MouseDrag, &mut V, &mut EventContext<V>) + 'static,
-    {
-        self.insert(MouseEvent::drag_disc(), Some(button),
-            Rc::new(move |region_event, view, cx, view_id| {
-                if let MouseEvent::Drag(e) = region_event {
-                    let view = view.downcast_mut().unwrap();
-                    let mut cx = ViewContext::mutable(cx, view_id);
-                    let mut cx = EventContext::new(&mut cx);
-                    handler(e, view, &mut cx);
-                    cx.handled
-                } else {
-                    panic!(
-                        "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Drag, found {:?}",
-                        region_event);
-                }
-            }));
-        self
-    }
-
-    pub fn on_hover<V, F>(mut self, handler: F) -> Self
-    where
-        V: 'static,
-        F: Fn(MouseHover, &mut V, &mut EventContext<V>) + 'static,
-    {
-        self.insert(MouseEvent::hover_disc(), None,
-            Rc::new(move |region_event, view, cx, view_id| {
-                if let MouseEvent::Hover(e) = region_event {
-                    let view = view.downcast_mut().unwrap();
-                    let mut cx = ViewContext::mutable(cx, view_id);
-                    let mut cx = EventContext::new(&mut cx);
-                    handler(e, view, &mut cx);
-                    cx.handled
-                } else {
-                    panic!(
-                        "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Hover, found {:?}",
-                        region_event);
-                }
-            }));
-        self
-    }
-
-    pub fn on_scroll<V, F>(mut self, handler: F) -> Self
-    where
-        V: 'static,
-        F: Fn(MouseScrollWheel, &mut V, &mut EventContext<V>) + 'static,
-    {
-        self.insert(MouseEvent::scroll_wheel_disc(), None,
-            Rc::new(move |region_event, view, cx, view_id| {
-                if let MouseEvent::ScrollWheel(e) = region_event {
-                    let view = view.downcast_mut().unwrap();
-                    let mut cx = ViewContext::mutable(cx, view_id);
-                    let mut cx = EventContext::new(&mut cx);
-                    handler(e, view, &mut cx);
-                    cx.handled
-                } else {
-                    panic!(
-                        "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::ScrollWheel, found {:?}",
-                        region_event
-                    );
-                }
-            }));
-        self
-    }
-}

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

@@ -1,7 +0,0 @@
-// use crate::geometry::rect::RectF;
-// use crate::WindowContext;
-
-// struct Region {
-//     pub bounds: RectF,
-//     pub click_handler: Option<Rc<dyn Fn(&dyn Any, MouseEvent, &mut WindowContext)>>,
-// }

crates/gpui/src/test.rs 🔗

@@ -1,186 +1,51 @@
+use crate::{Entity, Subscription, TestAppContext, TestDispatcher};
+use futures::StreamExt as _;
+use rand::prelude::*;
+use smol::channel;
 use std::{
-    fmt::Write,
+    env,
     panic::{self, RefUnwindSafe},
-    rc::Rc,
-    sync::{
-        atomic::{AtomicU64, Ordering::SeqCst},
-        Arc,
-    },
 };
 
-use futures::StreamExt;
-use parking_lot::Mutex;
-use smol::channel;
-
-use crate::{
-    app::ref_counts::LeakDetector,
-    elements::Empty,
-    executor::{self, ExecutorEvent},
-    platform,
-    platform::Platform,
-    util::CwdBacktrace,
-    AnyElement, AppContext, Element, Entity, FontCache, Handle, Subscription, TestAppContext, View,
-    ViewContext,
-};
-
-#[cfg(test)]
-#[ctor::ctor]
-fn init_logger() {
-    if std::env::var("RUST_LOG").is_ok() {
-        env_logger::init();
-    }
-}
-
-// #[global_allocator]
-// static ALLOC: dhat::Alloc = dhat::Alloc;
-
 pub fn run_test(
     mut num_iterations: u64,
-    mut starting_seed: u64,
     max_retries: usize,
-    detect_nondeterminism: bool,
-    test_fn: &mut (dyn RefUnwindSafe
-              + Fn(
-        &mut AppContext,
-        Rc<platform::test::ForegroundPlatform>,
-        Arc<executor::Deterministic>,
-        u64,
-    )),
+    test_fn: &mut (dyn RefUnwindSafe + Fn(TestDispatcher, u64)),
     on_fail_fn: Option<fn()>,
-    fn_name: String,
+    _fn_name: String, // todo!("re-enable fn_name")
 ) {
-    // let _profiler = dhat::Profiler::new_heap();
-
+    let starting_seed = env::var("SEED")
+        .map(|seed| seed.parse().expect("invalid SEED variable"))
+        .unwrap_or(0);
     let is_randomized = num_iterations > 1;
-    if is_randomized {
-        if let Ok(value) = std::env::var("SEED") {
-            starting_seed = value.parse().expect("invalid SEED variable");
-        }
-        if let Ok(value) = std::env::var("ITERATIONS") {
-            num_iterations = value.parse().expect("invalid ITERATIONS variable");
-        }
+    if let Ok(iterations) = env::var("ITERATIONS") {
+        num_iterations = iterations.parse().expect("invalid ITERATIONS variable");
     }
 
-    let atomic_seed = AtomicU64::new(starting_seed as u64);
-    let mut retries = 0;
-
-    loop {
-        let result = panic::catch_unwind(|| {
-            let foreground_platform = Rc::new(platform::test::foreground_platform());
-            let platform = Arc::new(platform::test::platform());
-            let font_system = platform.fonts();
-            let font_cache = Arc::new(FontCache::new(font_system));
-            let mut prev_runnable_history: Option<Vec<ExecutorEvent>> = None;
-
-            for _ in 0..num_iterations {
-                let seed = atomic_seed.load(SeqCst);
-
-                if is_randomized {
-                    eprintln!("seed = {seed}");
-                }
-
-                let deterministic = executor::Deterministic::new(seed);
-                if detect_nondeterminism {
-                    deterministic.set_previous_execution_history(prev_runnable_history.clone());
-                    deterministic.enable_runnable_backtrace();
-                }
-
-                let leak_detector = Arc::new(Mutex::new(LeakDetector::default()));
-                let mut cx = TestAppContext::new(
-                    foreground_platform.clone(),
-                    platform.clone(),
-                    deterministic.build_foreground(usize::MAX),
-                    deterministic.build_background(),
-                    font_cache.clone(),
-                    leak_detector.clone(),
-                    0,
-                    fn_name.clone(),
-                );
-                cx.update(|cx| {
-                    test_fn(cx, foreground_platform.clone(), deterministic.clone(), seed);
-                });
-
-                cx.remove_all_windows();
-                deterministic.run_until_parked();
-                cx.update(|cx| cx.clear_globals());
-
-                leak_detector.lock().detect();
-
-                if detect_nondeterminism {
-                    let curr_runnable_history = deterministic.execution_history();
-                    if let Some(prev_runnable_history) = prev_runnable_history {
-                        let mut prev_entries = prev_runnable_history.iter().fuse();
-                        let mut curr_entries = curr_runnable_history.iter().fuse();
-
-                        let mut nondeterministic = false;
-                        let mut common_history_prefix = Vec::new();
-                        let mut prev_history_suffix = Vec::new();
-                        let mut curr_history_suffix = Vec::new();
-                        loop {
-                            match (prev_entries.next(), curr_entries.next()) {
-                                (None, None) => break,
-                                (None, Some(curr_id)) => curr_history_suffix.push(*curr_id),
-                                (Some(prev_id), None) => prev_history_suffix.push(*prev_id),
-                                (Some(prev_id), Some(curr_id)) => {
-                                    if nondeterministic {
-                                        prev_history_suffix.push(*prev_id);
-                                        curr_history_suffix.push(*curr_id);
-                                    } else if prev_id == curr_id {
-                                        common_history_prefix.push(*curr_id);
-                                    } else {
-                                        nondeterministic = true;
-                                        prev_history_suffix.push(*prev_id);
-                                        curr_history_suffix.push(*curr_id);
-                                    }
-                                }
-                            }
-                        }
-
-                        if nondeterministic {
-                            let mut error = String::new();
-                            writeln!(&mut error, "Common prefix: {:?}", common_history_prefix)
-                                .unwrap();
-                            writeln!(&mut error, "Previous suffix: {:?}", prev_history_suffix)
-                                .unwrap();
-                            writeln!(&mut error, "Current suffix: {:?}", curr_history_suffix)
-                                .unwrap();
-
-                            let last_common_backtrace = common_history_prefix
-                                .last()
-                                .map(|event| deterministic.runnable_backtrace(event.id()));
-
-                            writeln!(
-                                &mut error,
-                                "Last future that ran on both executions: {:?}",
-                                last_common_backtrace.as_ref().map(CwdBacktrace)
-                            )
-                            .unwrap();
-                            panic!("Detected non-determinism.\n{}", error);
-                        }
-                    }
-                    prev_runnable_history = Some(curr_runnable_history);
-                }
-
-                if !detect_nondeterminism {
-                    atomic_seed.fetch_add(1, SeqCst);
-                }
+    for seed in starting_seed..starting_seed + num_iterations {
+        let mut retry = 0;
+        loop {
+            if is_randomized {
+                eprintln!("seed = {seed}");
             }
-        });
-
-        match result {
-            Ok(_) => {
-                break;
-            }
-            Err(error) => {
-                if retries < max_retries {
-                    retries += 1;
-                    println!("retrying: attempt {}", retries);
-                } else {
-                    if is_randomized {
-                        eprintln!("failing seed: {}", atomic_seed.load(SeqCst));
+            let result = panic::catch_unwind(|| {
+                let dispatcher = TestDispatcher::new(StdRng::seed_from_u64(seed));
+                test_fn(dispatcher, seed);
+            });
+
+            match result {
+                Ok(_) => break,
+                Err(error) => {
+                    if retry < max_retries {
+                        println!("retrying: attempt {}", retry);
+                        retry += 1;
+                    } else {
+                        if is_randomized {
+                            eprintln!("failing seed: {}", seed);
+                        }
+                        on_fail_fn.map(|f| f());
+                        panic::resume_unwind(error);
                     }
-                    on_fail_fn.map(|f| f());
-                    panic::resume_unwind(error);
                 }
             }
         }
@@ -192,7 +57,7 @@ pub struct Observation<T> {
     _subscription: Subscription,
 }
 
-impl<T> futures::Stream for Observation<T> {
+impl<T: 'static> futures::Stream for Observation<T> {
     type Item = T;
 
     fn poll_next(
@@ -203,7 +68,7 @@ impl<T> futures::Stream for Observation<T> {
     }
 }
 
-pub fn observe<T: Entity>(entity: &impl Handle<T>, cx: &mut TestAppContext) -> Observation<()> {
+pub fn observe<T: 'static>(entity: &impl Entity<T>, cx: &mut TestAppContext) -> Observation<()> {
     let (tx, rx) = smol::channel::unbounded();
     let _subscription = cx.update(|cx| {
         cx.observe(entity, move |_, _| {
@@ -213,36 +78,3 @@ pub fn observe<T: Entity>(entity: &impl Handle<T>, cx: &mut TestAppContext) -> O
 
     Observation { rx, _subscription }
 }
-
-pub fn subscribe<T: Entity>(
-    entity: &impl Handle<T>,
-    cx: &mut TestAppContext,
-) -> Observation<T::Event>
-where
-    T::Event: Clone,
-{
-    let (tx, rx) = smol::channel::unbounded();
-    let _subscription = cx.update(|cx| {
-        cx.subscribe(entity, move |_, event, _| {
-            let _ = smol::block_on(tx.send(event.clone()));
-        })
-    });
-
-    Observation { rx, _subscription }
-}
-
-pub struct EmptyView;
-
-impl Entity for EmptyView {
-    type Event = ();
-}
-
-impl View for EmptyView {
-    fn ui_name() -> &'static str {
-        "empty view"
-    }
-
-    fn render(&mut self, _: &mut ViewContext<Self>) -> AnyElement<Self> {
-        Empty::new().into_any()
-    }
-}

crates/gpui/src/text_layout.rs 🔗

@@ -1,876 +0,0 @@
-use crate::{
-    color::Color,
-    fonts::{FontId, GlyphId, Underline},
-    geometry::{
-        rect::RectF,
-        vector::{vec2f, Vector2F},
-    },
-    platform,
-    platform::FontSystem,
-    scene,
-    window::WindowContext,
-};
-use ordered_float::OrderedFloat;
-use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
-use smallvec::SmallVec;
-use std::{
-    borrow::Borrow,
-    collections::HashMap,
-    hash::{Hash, Hasher},
-    iter,
-    sync::Arc,
-};
-
-pub struct TextLayoutCache {
-    prev_frame: Mutex<HashMap<OwnedCacheKey, Arc<LineLayout>>>,
-    curr_frame: RwLock<HashMap<OwnedCacheKey, Arc<LineLayout>>>,
-    fonts: Arc<dyn platform::FontSystem>,
-}
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub struct RunStyle {
-    pub color: Color,
-    pub font_id: FontId,
-    pub underline: Underline,
-}
-
-impl TextLayoutCache {
-    pub fn new(fonts: Arc<dyn platform::FontSystem>) -> Self {
-        Self {
-            prev_frame: Mutex::new(HashMap::new()),
-            curr_frame: RwLock::new(HashMap::new()),
-            fonts,
-        }
-    }
-
-    pub fn finish_frame(&self) {
-        let mut prev_frame = self.prev_frame.lock();
-        let mut curr_frame = self.curr_frame.write();
-        std::mem::swap(&mut *prev_frame, &mut *curr_frame);
-        curr_frame.clear();
-    }
-
-    pub fn layout_str<'a>(
-        &'a self,
-        text: &'a str,
-        font_size: f32,
-        runs: &'a [(usize, RunStyle)],
-    ) -> Line {
-        let key = &BorrowedCacheKey {
-            text,
-            font_size: OrderedFloat(font_size),
-            runs,
-        } as &dyn CacheKey;
-        let curr_frame = self.curr_frame.upgradable_read();
-        if let Some(layout) = curr_frame.get(key) {
-            return Line::new(layout.clone(), runs);
-        }
-
-        let mut curr_frame = RwLockUpgradableReadGuard::upgrade(curr_frame);
-        if let Some((key, layout)) = self.prev_frame.lock().remove_entry(key) {
-            curr_frame.insert(key, layout.clone());
-            Line::new(layout, runs)
-        } else {
-            let layout = Arc::new(self.fonts.layout_line(text, font_size, runs));
-            let key = OwnedCacheKey {
-                text: text.into(),
-                font_size: OrderedFloat(font_size),
-                runs: SmallVec::from(runs),
-            };
-            curr_frame.insert(key, layout.clone());
-            Line::new(layout, runs)
-        }
-    }
-}
-
-trait CacheKey {
-    fn key(&self) -> BorrowedCacheKey;
-}
-
-impl<'a> PartialEq for (dyn CacheKey + 'a) {
-    fn eq(&self, other: &dyn CacheKey) -> bool {
-        self.key() == other.key()
-    }
-}
-
-impl<'a> Eq for (dyn CacheKey + 'a) {}
-
-impl<'a> Hash for (dyn CacheKey + 'a) {
-    fn hash<H: Hasher>(&self, state: &mut H) {
-        self.key().hash(state)
-    }
-}
-
-#[derive(Eq)]
-struct OwnedCacheKey {
-    text: String,
-    font_size: OrderedFloat<f32>,
-    runs: SmallVec<[(usize, RunStyle); 1]>,
-}
-
-impl CacheKey for OwnedCacheKey {
-    fn key(&self) -> BorrowedCacheKey {
-        BorrowedCacheKey {
-            text: self.text.as_str(),
-            font_size: self.font_size,
-            runs: self.runs.as_slice(),
-        }
-    }
-}
-
-impl PartialEq for OwnedCacheKey {
-    fn eq(&self, other: &Self) -> bool {
-        self.key().eq(&other.key())
-    }
-}
-
-impl Hash for OwnedCacheKey {
-    fn hash<H: Hasher>(&self, state: &mut H) {
-        self.key().hash(state);
-    }
-}
-
-impl<'a> Borrow<dyn CacheKey + 'a> for OwnedCacheKey {
-    fn borrow(&self) -> &(dyn CacheKey + 'a) {
-        self as &dyn CacheKey
-    }
-}
-
-#[derive(Copy, Clone)]
-struct BorrowedCacheKey<'a> {
-    text: &'a str,
-    font_size: OrderedFloat<f32>,
-    runs: &'a [(usize, RunStyle)],
-}
-
-impl<'a> CacheKey for BorrowedCacheKey<'a> {
-    fn key(&self) -> BorrowedCacheKey {
-        *self
-    }
-}
-
-impl<'a> PartialEq for BorrowedCacheKey<'a> {
-    fn eq(&self, other: &Self) -> bool {
-        self.text == other.text
-            && self.font_size == other.font_size
-            && self.runs.len() == other.runs.len()
-            && self.runs.iter().zip(other.runs.iter()).all(
-                |((len_a, style_a), (len_b, style_b))| {
-                    len_a == len_b && style_a.font_id == style_b.font_id
-                },
-            )
-    }
-}
-
-impl<'a> Hash for BorrowedCacheKey<'a> {
-    fn hash<H: Hasher>(&self, state: &mut H) {
-        self.text.hash(state);
-        self.font_size.hash(state);
-        for (len, style_id) in self.runs {
-            len.hash(state);
-            style_id.font_id.hash(state);
-        }
-    }
-}
-
-#[derive(Default, Debug, Clone)]
-pub struct Line {
-    layout: Arc<LineLayout>,
-    style_runs: SmallVec<[StyleRun; 32]>,
-}
-
-#[derive(Debug, Clone, Copy)]
-struct StyleRun {
-    len: u32,
-    color: Color,
-    underline: Underline,
-}
-
-#[derive(Default, Debug)]
-pub struct LineLayout {
-    pub width: f32,
-    pub ascent: f32,
-    pub descent: f32,
-    pub runs: Vec<Run>,
-    pub len: usize,
-    pub font_size: f32,
-}
-
-#[derive(Debug)]
-pub struct Run {
-    pub font_id: FontId,
-    pub glyphs: Vec<Glyph>,
-}
-
-#[derive(Clone, Debug)]
-pub struct Glyph {
-    pub id: GlyphId,
-    pub position: Vector2F,
-    pub index: usize,
-    pub is_emoji: bool,
-}
-
-impl Line {
-    pub fn new(layout: Arc<LineLayout>, runs: &[(usize, RunStyle)]) -> Self {
-        let mut style_runs = SmallVec::new();
-        for (len, style) in runs {
-            style_runs.push(StyleRun {
-                len: *len as u32,
-                color: style.color,
-                underline: style.underline,
-            });
-        }
-        Self { layout, style_runs }
-    }
-
-    pub fn runs(&self) -> &[Run] {
-        &self.layout.runs
-    }
-
-    pub fn width(&self) -> f32 {
-        self.layout.width
-    }
-
-    pub fn font_size(&self) -> f32 {
-        self.layout.font_size
-    }
-
-    pub fn x_for_index(&self, index: usize) -> f32 {
-        for run in &self.layout.runs {
-            for glyph in &run.glyphs {
-                if glyph.index >= index {
-                    return glyph.position.x();
-                }
-            }
-        }
-        self.layout.width
-    }
-
-    pub fn font_for_index(&self, index: usize) -> Option<FontId> {
-        for run in &self.layout.runs {
-            for glyph in &run.glyphs {
-                if glyph.index >= index {
-                    return Some(run.font_id);
-                }
-            }
-        }
-
-        None
-    }
-
-    pub fn len(&self) -> usize {
-        self.layout.len
-    }
-
-    pub fn is_empty(&self) -> bool {
-        self.layout.len == 0
-    }
-
-    /// index_for_x returns the character containing the given x coordinate.
-    /// (e.g. to handle a mouse-click)
-    pub fn index_for_x(&self, x: f32) -> Option<usize> {
-        if x >= self.layout.width {
-            None
-        } else {
-            for run in self.layout.runs.iter().rev() {
-                for glyph in run.glyphs.iter().rev() {
-                    if glyph.position.x() <= x {
-                        return Some(glyph.index);
-                    }
-                }
-            }
-            Some(0)
-        }
-    }
-
-    /// closest_index_for_x returns the character boundary closest to the given x coordinate
-    /// (e.g. to handle aligning up/down arrow keys)
-    pub fn closest_index_for_x(&self, x: f32) -> usize {
-        let mut prev_index = 0;
-        let mut prev_x = 0.0;
-
-        for run in self.layout.runs.iter() {
-            for glyph in run.glyphs.iter() {
-                if glyph.position.x() >= x {
-                    if glyph.position.x() - x < x - prev_x {
-                        return glyph.index;
-                    } else {
-                        return prev_index;
-                    }
-                }
-                prev_index = glyph.index;
-                prev_x = glyph.position.x();
-            }
-        }
-        prev_index
-    }
-
-    pub fn paint(
-        &self,
-        origin: Vector2F,
-        visible_bounds: RectF,
-        line_height: f32,
-        cx: &mut WindowContext,
-    ) {
-        let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.;
-        let baseline_offset = vec2f(0., padding_top + self.layout.ascent);
-
-        let mut style_runs = self.style_runs.iter();
-        let mut run_end = 0;
-        let mut color = Color::black();
-        let mut underline = None;
-
-        for run in &self.layout.runs {
-            let max_glyph_width = cx
-                .font_cache
-                .bounding_box(run.font_id, self.layout.font_size)
-                .x();
-
-            for glyph in &run.glyphs {
-                let glyph_origin = origin + baseline_offset + glyph.position;
-                if glyph_origin.x() > visible_bounds.upper_right().x() {
-                    break;
-                }
-
-                let mut finished_underline = None;
-                if glyph.index >= run_end {
-                    if let Some(style_run) = style_runs.next() {
-                        if let Some((_, underline_style)) = underline {
-                            if style_run.underline != underline_style {
-                                finished_underline = underline.take();
-                            }
-                        }
-                        if style_run.underline.thickness.into_inner() > 0. {
-                            underline.get_or_insert((
-                                vec2f(
-                                    glyph_origin.x(),
-                                    origin.y() + baseline_offset.y() + 0.618 * self.layout.descent,
-                                ),
-                                Underline {
-                                    color: Some(
-                                        style_run.underline.color.unwrap_or(style_run.color),
-                                    ),
-                                    thickness: style_run.underline.thickness,
-                                    squiggly: style_run.underline.squiggly,
-                                },
-                            ));
-                        }
-
-                        run_end += style_run.len as usize;
-                        color = style_run.color;
-                    } else {
-                        run_end = self.layout.len;
-                        finished_underline = underline.take();
-                    }
-                }
-
-                if glyph_origin.x() + max_glyph_width < visible_bounds.origin().x() {
-                    continue;
-                }
-
-                if let Some((underline_origin, underline_style)) = finished_underline {
-                    cx.scene().push_underline(scene::Underline {
-                        origin: underline_origin,
-                        width: glyph_origin.x() - underline_origin.x(),
-                        thickness: underline_style.thickness.into(),
-                        color: underline_style.color.unwrap(),
-                        squiggly: underline_style.squiggly,
-                    });
-                }
-
-                if glyph.is_emoji {
-                    cx.scene().push_image_glyph(scene::ImageGlyph {
-                        font_id: run.font_id,
-                        font_size: self.layout.font_size,
-                        id: glyph.id,
-                        origin: glyph_origin,
-                    });
-                } else {
-                    cx.scene().push_glyph(scene::Glyph {
-                        font_id: run.font_id,
-                        font_size: self.layout.font_size,
-                        id: glyph.id,
-                        origin: glyph_origin,
-                        color,
-                    });
-                }
-            }
-        }
-
-        if let Some((underline_start, underline_style)) = underline.take() {
-            let line_end_x = origin.x() + self.layout.width;
-            cx.scene().push_underline(scene::Underline {
-                origin: underline_start,
-                width: line_end_x - underline_start.x(),
-                color: underline_style.color.unwrap(),
-                thickness: underline_style.thickness.into(),
-                squiggly: underline_style.squiggly,
-            });
-        }
-    }
-
-    pub fn paint_wrapped(
-        &self,
-        origin: Vector2F,
-        visible_bounds: RectF,
-        line_height: f32,
-        boundaries: &[ShapedBoundary],
-        cx: &mut WindowContext,
-    ) {
-        let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.;
-        let baseline_offset = vec2f(0., padding_top + self.layout.ascent);
-
-        let mut boundaries = boundaries.into_iter().peekable();
-        let mut color_runs = self.style_runs.iter();
-        let mut style_run_end = 0;
-        let mut color = Color::black();
-        let mut underline: Option<(Vector2F, Underline)> = None;
-
-        let mut glyph_origin = origin;
-        let mut prev_position = 0.;
-        for (run_ix, run) in self.layout.runs.iter().enumerate() {
-            for (glyph_ix, glyph) in run.glyphs.iter().enumerate() {
-                glyph_origin.set_x(glyph_origin.x() + glyph.position.x() - prev_position);
-
-                if boundaries
-                    .peek()
-                    .map_or(false, |b| b.run_ix == run_ix && b.glyph_ix == glyph_ix)
-                {
-                    boundaries.next();
-                    if let Some((underline_origin, underline_style)) = underline {
-                        cx.scene().push_underline(scene::Underline {
-                            origin: underline_origin,
-                            width: glyph_origin.x() - underline_origin.x(),
-                            thickness: underline_style.thickness.into(),
-                            color: underline_style.color.unwrap(),
-                            squiggly: underline_style.squiggly,
-                        });
-                    }
-
-                    glyph_origin = vec2f(origin.x(), glyph_origin.y() + line_height);
-                }
-                prev_position = glyph.position.x();
-
-                let mut finished_underline = None;
-                if glyph.index >= style_run_end {
-                    if let Some(style_run) = color_runs.next() {
-                        style_run_end += style_run.len as usize;
-                        color = style_run.color;
-                        if let Some((_, underline_style)) = underline {
-                            if style_run.underline != underline_style {
-                                finished_underline = underline.take();
-                            }
-                        }
-                        if style_run.underline.thickness.into_inner() > 0. {
-                            underline.get_or_insert((
-                                glyph_origin
-                                    + vec2f(0., baseline_offset.y() + 0.618 * self.layout.descent),
-                                Underline {
-                                    color: Some(
-                                        style_run.underline.color.unwrap_or(style_run.color),
-                                    ),
-                                    thickness: style_run.underline.thickness,
-                                    squiggly: style_run.underline.squiggly,
-                                },
-                            ));
-                        }
-                    } else {
-                        style_run_end = self.layout.len;
-                        color = Color::black();
-                        finished_underline = underline.take();
-                    }
-                }
-
-                if let Some((underline_origin, underline_style)) = finished_underline {
-                    cx.scene().push_underline(scene::Underline {
-                        origin: underline_origin,
-                        width: glyph_origin.x() - underline_origin.x(),
-                        thickness: underline_style.thickness.into(),
-                        color: underline_style.color.unwrap(),
-                        squiggly: underline_style.squiggly,
-                    });
-                }
-
-                let glyph_bounds = RectF::new(
-                    glyph_origin,
-                    cx.font_cache
-                        .bounding_box(run.font_id, self.layout.font_size),
-                );
-                if glyph_bounds.intersects(visible_bounds) {
-                    if glyph.is_emoji {
-                        cx.scene().push_image_glyph(scene::ImageGlyph {
-                            font_id: run.font_id,
-                            font_size: self.layout.font_size,
-                            id: glyph.id,
-                            origin: glyph_bounds.origin() + baseline_offset,
-                        });
-                    } else {
-                        cx.scene().push_glyph(scene::Glyph {
-                            font_id: run.font_id,
-                            font_size: self.layout.font_size,
-                            id: glyph.id,
-                            origin: glyph_bounds.origin() + baseline_offset,
-                            color,
-                        });
-                    }
-                }
-            }
-        }
-
-        if let Some((underline_origin, underline_style)) = underline.take() {
-            let line_end_x = glyph_origin.x() + self.layout.width - prev_position;
-            cx.scene().push_underline(scene::Underline {
-                origin: underline_origin,
-                width: line_end_x - underline_origin.x(),
-                thickness: underline_style.thickness.into(),
-                color: underline_style.color.unwrap(),
-                squiggly: underline_style.squiggly,
-            });
-        }
-    }
-}
-
-impl Run {
-    pub fn glyphs(&self) -> &[Glyph] {
-        &self.glyphs
-    }
-}
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub struct Boundary {
-    pub ix: usize,
-    pub next_indent: u32,
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
-pub struct ShapedBoundary {
-    pub run_ix: usize,
-    pub glyph_ix: usize,
-}
-
-impl Boundary {
-    fn new(ix: usize, next_indent: u32) -> Self {
-        Self { ix, next_indent }
-    }
-}
-
-pub struct LineWrapper {
-    font_system: Arc<dyn FontSystem>,
-    pub(crate) font_id: FontId,
-    pub(crate) font_size: f32,
-    cached_ascii_char_widths: [f32; 128],
-    cached_other_char_widths: HashMap<char, f32>,
-}
-
-impl LineWrapper {
-    pub const MAX_INDENT: u32 = 256;
-
-    pub fn new(font_id: FontId, font_size: f32, font_system: Arc<dyn FontSystem>) -> Self {
-        Self {
-            font_system,
-            font_id,
-            font_size,
-            cached_ascii_char_widths: [f32::NAN; 128],
-            cached_other_char_widths: HashMap::new(),
-        }
-    }
-
-    pub fn wrap_line<'a>(
-        &'a mut self,
-        line: &'a str,
-        wrap_width: f32,
-    ) -> impl Iterator<Item = Boundary> + 'a {
-        let mut width = 0.0;
-        let mut first_non_whitespace_ix = None;
-        let mut indent = None;
-        let mut last_candidate_ix = 0;
-        let mut last_candidate_width = 0.0;
-        let mut last_wrap_ix = 0;
-        let mut prev_c = '\0';
-        let mut char_indices = line.char_indices();
-        iter::from_fn(move || {
-            for (ix, c) in char_indices.by_ref() {
-                if c == '\n' {
-                    continue;
-                }
-
-                if self.is_boundary(prev_c, c) && first_non_whitespace_ix.is_some() {
-                    last_candidate_ix = ix;
-                    last_candidate_width = width;
-                }
-
-                if c != ' ' && first_non_whitespace_ix.is_none() {
-                    first_non_whitespace_ix = Some(ix);
-                }
-
-                let char_width = self.width_for_char(c);
-                width += char_width;
-                if width > wrap_width && ix > last_wrap_ix {
-                    if let (None, Some(first_non_whitespace_ix)) = (indent, first_non_whitespace_ix)
-                    {
-                        indent = Some(
-                            Self::MAX_INDENT.min((first_non_whitespace_ix - last_wrap_ix) as u32),
-                        );
-                    }
-
-                    if last_candidate_ix > 0 {
-                        last_wrap_ix = last_candidate_ix;
-                        width -= last_candidate_width;
-                        last_candidate_ix = 0;
-                    } else {
-                        last_wrap_ix = ix;
-                        width = char_width;
-                    }
-
-                    let indent_width =
-                        indent.map(|indent| indent as f32 * self.width_for_char(' '));
-                    width += indent_width.unwrap_or(0.);
-
-                    return Some(Boundary::new(last_wrap_ix, indent.unwrap_or(0)));
-                }
-                prev_c = c;
-            }
-
-            None
-        })
-    }
-
-    pub fn wrap_shaped_line<'a>(
-        &'a mut self,
-        str: &'a str,
-        line: &'a Line,
-        wrap_width: f32,
-    ) -> impl Iterator<Item = ShapedBoundary> + 'a {
-        let mut first_non_whitespace_ix = None;
-        let mut last_candidate_ix = None;
-        let mut last_candidate_x = 0.0;
-        let mut last_wrap_ix = ShapedBoundary {
-            run_ix: 0,
-            glyph_ix: 0,
-        };
-        let mut last_wrap_x = 0.;
-        let mut prev_c = '\0';
-        let mut glyphs = line
-            .runs()
-            .iter()
-            .enumerate()
-            .flat_map(move |(run_ix, run)| {
-                run.glyphs()
-                    .iter()
-                    .enumerate()
-                    .map(move |(glyph_ix, glyph)| {
-                        let character = str[glyph.index..].chars().next().unwrap();
-                        (
-                            ShapedBoundary { run_ix, glyph_ix },
-                            character,
-                            glyph.position.x(),
-                        )
-                    })
-            })
-            .peekable();
-
-        iter::from_fn(move || {
-            while let Some((ix, c, x)) = glyphs.next() {
-                if c == '\n' {
-                    continue;
-                }
-
-                if self.is_boundary(prev_c, c) && first_non_whitespace_ix.is_some() {
-                    last_candidate_ix = Some(ix);
-                    last_candidate_x = x;
-                }
-
-                if c != ' ' && first_non_whitespace_ix.is_none() {
-                    first_non_whitespace_ix = Some(ix);
-                }
-
-                let next_x = glyphs.peek().map_or(line.width(), |(_, _, x)| *x);
-                let width = next_x - last_wrap_x;
-                if width > wrap_width && ix > last_wrap_ix {
-                    if let Some(last_candidate_ix) = last_candidate_ix.take() {
-                        last_wrap_ix = last_candidate_ix;
-                        last_wrap_x = last_candidate_x;
-                    } else {
-                        last_wrap_ix = ix;
-                        last_wrap_x = x;
-                    }
-
-                    return Some(last_wrap_ix);
-                }
-                prev_c = c;
-            }
-
-            None
-        })
-    }
-
-    fn is_boundary(&self, prev: char, next: char) -> bool {
-        (prev == ' ') && (next != ' ')
-    }
-
-    #[inline(always)]
-    fn width_for_char(&mut self, c: char) -> f32 {
-        if (c as u32) < 128 {
-            let mut width = self.cached_ascii_char_widths[c as usize];
-            if width.is_nan() {
-                width = self.compute_width_for_char(c);
-                self.cached_ascii_char_widths[c as usize] = width;
-            }
-            width
-        } else {
-            let mut width = self
-                .cached_other_char_widths
-                .get(&c)
-                .copied()
-                .unwrap_or(f32::NAN);
-            if width.is_nan() {
-                width = self.compute_width_for_char(c);
-                self.cached_other_char_widths.insert(c, width);
-            }
-            width
-        }
-    }
-
-    fn compute_width_for_char(&self, c: char) -> f32 {
-        self.font_system
-            .layout_line(
-                &c.to_string(),
-                self.font_size,
-                &[(
-                    1,
-                    RunStyle {
-                        font_id: self.font_id,
-                        color: Default::default(),
-                        underline: Default::default(),
-                    },
-                )],
-            )
-            .width
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::fonts::{Properties, Weight};
-
-    #[crate::test(self)]
-    fn test_wrap_line(cx: &mut crate::AppContext) {
-        let font_cache = cx.font_cache().clone();
-        let font_system = cx.platform().fonts();
-        let family = font_cache
-            .load_family(&["Courier"], &Default::default())
-            .unwrap();
-        let font_id = font_cache.select_font(family, &Default::default()).unwrap();
-
-        let mut wrapper = LineWrapper::new(font_id, 16., font_system);
-        assert_eq!(
-            wrapper
-                .wrap_line("aa bbb cccc ddddd eeee", 72.0)
-                .collect::<Vec<_>>(),
-            &[
-                Boundary::new(7, 0),
-                Boundary::new(12, 0),
-                Boundary::new(18, 0)
-            ],
-        );
-        assert_eq!(
-            wrapper
-                .wrap_line("aaa aaaaaaaaaaaaaaaaaa", 72.0)
-                .collect::<Vec<_>>(),
-            &[
-                Boundary::new(4, 0),
-                Boundary::new(11, 0),
-                Boundary::new(18, 0)
-            ],
-        );
-        assert_eq!(
-            wrapper.wrap_line("     aaaaaaa", 72.).collect::<Vec<_>>(),
-            &[
-                Boundary::new(7, 5),
-                Boundary::new(9, 5),
-                Boundary::new(11, 5),
-            ]
-        );
-        assert_eq!(
-            wrapper
-                .wrap_line("                            ", 72.)
-                .collect::<Vec<_>>(),
-            &[
-                Boundary::new(7, 0),
-                Boundary::new(14, 0),
-                Boundary::new(21, 0)
-            ]
-        );
-        assert_eq!(
-            wrapper
-                .wrap_line("          aaaaaaaaaaaaaa", 72.)
-                .collect::<Vec<_>>(),
-            &[
-                Boundary::new(7, 0),
-                Boundary::new(14, 3),
-                Boundary::new(18, 3),
-                Boundary::new(22, 3),
-            ]
-        );
-    }
-
-    #[crate::test(self, retries = 5)]
-    fn test_wrap_shaped_line(cx: &mut crate::AppContext) {
-        // This is failing intermittently on CI and we don't have time to figure it out
-        let font_cache = cx.font_cache().clone();
-        let font_system = cx.platform().fonts();
-        let text_layout_cache = TextLayoutCache::new(font_system.clone());
-
-        let family = font_cache
-            .load_family(&["Helvetica"], &Default::default())
-            .unwrap();
-        let font_id = font_cache.select_font(family, &Default::default()).unwrap();
-        let normal = RunStyle {
-            font_id,
-            color: Default::default(),
-            underline: Default::default(),
-        };
-        let bold = RunStyle {
-            font_id: font_cache
-                .select_font(
-                    family,
-                    &Properties {
-                        weight: Weight::BOLD,
-                        ..Default::default()
-                    },
-                )
-                .unwrap(),
-            color: Default::default(),
-            underline: Default::default(),
-        };
-
-        let text = "aa bbb cccc ddddd eeee";
-        let line = text_layout_cache.layout_str(
-            text,
-            16.0,
-            &[(4, normal), (5, bold), (6, normal), (1, bold), (7, normal)],
-        );
-
-        let mut wrapper = LineWrapper::new(font_id, 16., font_system);
-        assert_eq!(
-            wrapper
-                .wrap_shaped_line(text, &line, 72.0)
-                .collect::<Vec<_>>(),
-            &[
-                ShapedBoundary {
-                    run_ix: 1,
-                    glyph_ix: 3
-                },
-                ShapedBoundary {
-                    run_ix: 2,
-                    glyph_ix: 3
-                },
-                ShapedBoundary {
-                    run_ix: 4,
-                    glyph_ix: 2
-                }
-            ],
-        );
-    }
-}

crates/gpui/src/util.rs 🔗

@@ -1,12 +1,15 @@
+#[cfg(any(test, feature = "test-support"))]
+use std::time::Duration;
+
+#[cfg(any(test, feature = "test-support"))]
+use futures::Future;
+
+#[cfg(any(test, feature = "test-support"))]
 use smol::future::FutureExt;
-use std::{future::Future, time::Duration};
 
-pub fn post_inc(value: &mut usize) -> usize {
-    let prev = *value;
-    *value += 1;
-    prev
-}
+pub use util::*;
 
+#[cfg(any(test, feature = "test-support"))]
 pub async fn timeout<F, T>(timeout: Duration, f: F) -> Result<T, ()>
 where
     F: Future<Output = T>,

crates/gpui/src/views.rs 🔗

@@ -1,5 +0,0 @@
-mod select;
-
-pub use select::{ItemType, Select, SelectStyle};
-
-pub fn init(_: &mut super::AppContext) {}

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

@@ -1,156 +0,0 @@
-use crate::{
-    elements::*,
-    platform::{CursorStyle, MouseButton},
-    AppContext, Entity, View, ViewContext, WeakViewHandle,
-};
-
-pub struct Select {
-    handle: WeakViewHandle<Self>,
-    render_item: Box<dyn Fn(usize, ItemType, bool, &mut ViewContext<Select>) -> AnyElement<Self>>,
-    selected_item_ix: usize,
-    item_count: usize,
-    is_open: bool,
-    list_state: UniformListState,
-    build_style: Option<Box<dyn FnMut(&mut AppContext) -> SelectStyle>>,
-}
-
-#[derive(Clone, Default)]
-pub struct SelectStyle {
-    pub header: ContainerStyle,
-    pub menu: ContainerStyle,
-}
-
-pub enum ItemType {
-    Header,
-    Selected,
-    Unselected,
-}
-
-pub enum Event {}
-
-impl Select {
-    pub fn new<
-        F: 'static + Fn(usize, ItemType, bool, &mut ViewContext<Self>) -> AnyElement<Self>,
-    >(
-        item_count: usize,
-        cx: &mut ViewContext<Self>,
-        render_item: F,
-    ) -> Self {
-        Self {
-            handle: cx.weak_handle(),
-            render_item: Box::new(render_item),
-            selected_item_ix: 0,
-            item_count,
-            is_open: false,
-            list_state: UniformListState::default(),
-            build_style: Default::default(),
-        }
-    }
-
-    pub fn with_style(mut self, f: impl 'static + FnMut(&mut AppContext) -> SelectStyle) -> Self {
-        self.build_style = Some(Box::new(f));
-        self
-    }
-
-    pub fn set_item_count(&mut self, count: usize, cx: &mut ViewContext<Self>) {
-        if count != self.item_count {
-            self.item_count = count;
-            cx.notify();
-        }
-    }
-
-    fn toggle(&mut self, cx: &mut ViewContext<Self>) {
-        self.is_open = !self.is_open;
-        cx.notify();
-    }
-
-    pub fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Self>) {
-        if ix != self.selected_item_ix || self.is_open {
-            self.selected_item_ix = ix;
-            self.is_open = false;
-            cx.notify();
-        }
-    }
-
-    pub fn selected_index(&self) -> usize {
-        self.selected_item_ix
-    }
-}
-
-impl Entity for Select {
-    type Event = Event;
-}
-
-impl View for Select {
-    fn ui_name() -> &'static str {
-        "Select"
-    }
-
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
-        if self.item_count == 0 {
-            return Empty::new().into_any();
-        }
-
-        enum Header {}
-        enum Item {}
-
-        let style = if let Some(build_style) = self.build_style.as_mut() {
-            (build_style)(cx)
-        } else {
-            Default::default()
-        };
-        let mut result = Flex::column().with_child(
-            MouseEventHandler::new::<Header, _>(self.handle.id(), cx, |mouse_state, cx| {
-                (self.render_item)(
-                    self.selected_item_ix,
-                    ItemType::Header,
-                    mouse_state.hovered(),
-                    cx,
-                )
-                .contained()
-                .with_style(style.header)
-            })
-            .with_cursor_style(CursorStyle::PointingHand)
-            .on_click(MouseButton::Left, move |_, this, cx| {
-                this.toggle(cx);
-            }),
-        );
-        if self.is_open {
-            result.add_child(Overlay::new(
-                UniformList::new(
-                    self.list_state.clone(),
-                    self.item_count,
-                    cx,
-                    move |this, mut range, items, cx| {
-                        let selected_item_ix = this.selected_item_ix;
-                        range.end = range.end.min(this.item_count);
-                        items.extend(range.map(|ix| {
-                            MouseEventHandler::new::<Item, _>(ix, cx, |mouse_state, cx| {
-                                (this.render_item)(
-                                    ix,
-                                    if ix == selected_item_ix {
-                                        ItemType::Selected
-                                    } else {
-                                        ItemType::Unselected
-                                    },
-                                    mouse_state.hovered(),
-                                    cx,
-                                )
-                            })
-                            .with_cursor_style(CursorStyle::PointingHand)
-                            .on_click(MouseButton::Left, move |_, this, cx| {
-                                this.set_selected_index(ix, cx);
-                            })
-                            .into_any()
-                        }))
-                    },
-                )
-                .constrained()
-                .with_max_height(200.)
-                .contained()
-                .with_style(style.menu),
-            ));
-        }
-        result.into_any()
-    }
-}

crates/gpui/tests/test.rs 🔗

@@ -1,14 +0,0 @@
-use gpui::{elements::Empty, Element, ViewContext};
-// use gpui_macros::Element;
-
-#[test]
-fn test_derive_render_element() {
-    #[derive(Element)]
-    struct TestElement {}
-
-    impl TestElement {
-        fn render<V: 'static>(&mut self, _: &mut V, _: &mut ViewContext<V>) -> impl Element<V> {
-            Empty::new()
-        }
-    }
-}

crates/gpui2/Cargo.toml 🔗

@@ -1,92 +0,0 @@
-[package]
-name = "gpui2"
-version = "0.1.0"
-edition = "2021"
-authors = ["Nathan Sobo <nathan@zed.dev>"]
-description = "The next version of Zed's GPU-accelerated UI framework"
-publish = false
-
-[features]
-test-support = ["backtrace", "dhat", "env_logger", "collections/test-support", "util/test-support"]
-
-# Suppress a panic when both GPUI1 and GPUI2 are loaded.
-#
-# This is used in the `theme_importer` where we need to depend on both
-# GPUI1 and GPUI2 in order to convert Zed1 themes to Zed2 themes.
-allow-multiple-gpui-versions = ["util/allow-multiple-gpui-versions"]
-
-[lib]
-path = "src/gpui2.rs"
-doctest = false
-
-[dependencies]
-collections = { path = "../collections" }
-gpui2_macros = { path = "../gpui2_macros" }
-util = { path = "../util" }
-sum_tree = { path = "../sum_tree" }
-sqlez = { path = "../sqlez" }
-async-task = "4.0.3"
-backtrace = { version = "0.3", optional = true }
-ctor.workspace = true
-linkme = "0.3"
-derive_more.workspace = true
-dhat = { version = "0.3", optional = true }
-env_logger = { version = "0.9", optional = true }
-etagere = "0.2"
-futures.workspace = true
-image = "0.23"
-itertools = "0.10"
-lazy_static.workspace = true
-log.workspace = true
-num_cpus = "1.13"
-ordered-float.workspace = true
-parking = "2.0.0"
-parking_lot.workspace = true
-pathfinder_geometry = "0.5"
-postage.workspace = true
-rand.workspace = true
-refineable.workspace = true
-resvg = "0.14"
-seahash = "4.1"
-serde.workspace = true
-serde_derive.workspace = true
-serde_json.workspace = true
-smallvec.workspace = true
-smol.workspace = true
-taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "1876f72bee5e376023eaa518aa7b8a34c769bd1b" }
-thiserror.workspace = true
-time.workspace = true
-tiny-skia = "0.5"
-usvg = { version = "0.14", features = [] }
-uuid = { version = "1.1.2", features = ["v4"] }
-waker-fn = "1.1.0"
-slotmap = "1.0.6"
-schemars.workspace = true
-bitflags = "2.4.0"
-
-[dev-dependencies]
-backtrace = "0.3"
-collections = { path = "../collections", features = ["test-support"] }
-dhat = "0.3"
-env_logger.workspace = true
-png = "0.16"
-simplelog = "0.9"
-util = { path = "../util", features = ["test-support"] }
-
-[build-dependencies]
-bindgen = "0.65.1"
-cbindgen = "0.26.0"
-
-[target.'cfg(target_os = "macos")'.dependencies]
-media = { path = "../media" }
-anyhow.workspace = true
-block = "0.1"
-cocoa = "0.24"
-core-foundation = { version = "0.9.3", features = ["with-uuid"] }
-core-graphics = "0.22.3"
-core-text = "19.2"
-font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "b2f77d56f450338aa4f7dd2f0197d8c9acb0cf18" }
-foreign-types = "0.3"
-log.workspace = true
-metal = "0.21.0"
-objc = "0.2"

crates/gpui2/build.rs 🔗

@@ -1,137 +0,0 @@
-use std::{
-    env,
-    path::{Path, PathBuf},
-    process::{self, Command},
-};
-
-use cbindgen::Config;
-
-fn main() {
-    generate_dispatch_bindings();
-    let header_path = generate_shader_bindings();
-    compile_metal_shaders(&header_path);
-}
-
-fn generate_dispatch_bindings() {
-    println!("cargo:rustc-link-lib=framework=System");
-    println!("cargo:rerun-if-changed=src/platform/mac/dispatch.h");
-
-    let bindings = bindgen::Builder::default()
-        .header("src/platform/mac/dispatch.h")
-        .allowlist_var("_dispatch_main_q")
-        .allowlist_var("DISPATCH_QUEUE_PRIORITY_DEFAULT")
-        .allowlist_var("DISPATCH_TIME_NOW")
-        .allowlist_function("dispatch_get_global_queue")
-        .allowlist_function("dispatch_async_f")
-        .allowlist_function("dispatch_after_f")
-        .allowlist_function("dispatch_time")
-        .parse_callbacks(Box::new(bindgen::CargoCallbacks))
-        .layout_tests(false)
-        .generate()
-        .expect("unable to generate bindings");
-
-    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
-    bindings
-        .write_to_file(out_path.join("dispatch_sys.rs"))
-        .expect("couldn't write dispatch bindings");
-}
-
-fn generate_shader_bindings() -> PathBuf {
-    let output_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("scene.h");
-    let crate_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
-    let mut config = Config::default();
-    config.include_guard = Some("SCENE_H".into());
-    config.language = cbindgen::Language::C;
-    config.export.include.extend([
-        "Bounds".into(),
-        "Corners".into(),
-        "Edges".into(),
-        "Size".into(),
-        "Pixels".into(),
-        "PointF".into(),
-        "Hsla".into(),
-        "ContentMask".into(),
-        "Uniforms".into(),
-        "AtlasTile".into(),
-        "PathRasterizationInputIndex".into(),
-        "PathVertex_ScaledPixels".into(),
-        "ShadowInputIndex".into(),
-        "Shadow".into(),
-        "QuadInputIndex".into(),
-        "Underline".into(),
-        "UnderlineInputIndex".into(),
-        "Quad".into(),
-        "SpriteInputIndex".into(),
-        "MonochromeSprite".into(),
-        "PolychromeSprite".into(),
-        "PathSprite".into(),
-        "SurfaceInputIndex".into(),
-        "SurfaceBounds".into(),
-    ]);
-    config.no_includes = true;
-    config.enumeration.prefix_with_name = true;
-    cbindgen::Builder::new()
-        .with_src(crate_dir.join("src/scene.rs"))
-        .with_src(crate_dir.join("src/geometry.rs"))
-        .with_src(crate_dir.join("src/color.rs"))
-        .with_src(crate_dir.join("src/window.rs"))
-        .with_src(crate_dir.join("src/platform.rs"))
-        .with_src(crate_dir.join("src/platform/mac/metal_renderer.rs"))
-        .with_config(config)
-        .generate()
-        .expect("Unable to generate bindings")
-        .write_to_file(&output_path);
-
-    output_path
-}
-
-fn compile_metal_shaders(header_path: &Path) {
-    let shader_path = "./src/platform/mac/shaders.metal";
-    let air_output_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("shaders.air");
-    let metallib_output_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("shaders.metallib");
-
-    println!("cargo:rerun-if-changed={}", header_path.display());
-    println!("cargo:rerun-if-changed={}", shader_path);
-
-    let output = Command::new("xcrun")
-        .args([
-            "-sdk",
-            "macosx",
-            "metal",
-            "-gline-tables-only",
-            "-mmacosx-version-min=10.15.7",
-            "-MO",
-            "-c",
-            shader_path,
-            "-include",
-            &header_path.to_str().unwrap(),
-            "-o",
-        ])
-        .arg(&air_output_path)
-        .output()
-        .unwrap();
-
-    if !output.status.success() {
-        eprintln!(
-            "metal shader compilation failed:\n{}",
-            String::from_utf8_lossy(&output.stderr)
-        );
-        process::exit(1);
-    }
-
-    let output = Command::new("xcrun")
-        .args(["-sdk", "macosx", "metallib"])
-        .arg(air_output_path)
-        .arg("-o")
-        .arg(metallib_output_path)
-        .output()
-        .unwrap();
-
-    if !output.status.success() {
-        eprintln!(
-            "metallib compilation failed:\n{}",
-            String::from_utf8_lossy(&output.stderr)
-        );
-        process::exit(1);
-    }
-}

crates/gpui2/src/app.rs 🔗

@@ -1,1264 +0,0 @@
-mod async_context;
-mod entity_map;
-mod model_context;
-#[cfg(any(test, feature = "test-support"))]
-mod test_context;
-
-pub use async_context::*;
-use derive_more::{Deref, DerefMut};
-pub use entity_map::*;
-pub use model_context::*;
-use refineable::Refineable;
-use smol::future::FutureExt;
-#[cfg(any(test, feature = "test-support"))]
-pub use test_context::*;
-use time::UtcOffset;
-
-use crate::{
-    current_platform, image_cache::ImageCache, init_app_menus, Action, ActionRegistry, Any,
-    AnyView, AnyWindowHandle, AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context,
-    DispatchPhase, DisplayId, Entity, EventEmitter, ForegroundExecutor, KeyBinding, Keymap,
-    Keystroke, LayoutId, Menu, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, Render,
-    SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement,
-    TextSystem, View, ViewContext, Window, WindowContext, WindowHandle, WindowId,
-};
-use anyhow::{anyhow, Result};
-use collections::{FxHashMap, FxHashSet, VecDeque};
-use futures::{channel::oneshot, future::LocalBoxFuture, Future};
-use parking_lot::Mutex;
-use slotmap::SlotMap;
-use std::{
-    any::{type_name, TypeId},
-    cell::{Ref, RefCell, RefMut},
-    marker::PhantomData,
-    mem,
-    ops::{Deref, DerefMut},
-    path::{Path, PathBuf},
-    rc::{Rc, Weak},
-    sync::{atomic::Ordering::SeqCst, Arc},
-    time::Duration,
-};
-use util::{
-    http::{self, HttpClient},
-    ResultExt,
-};
-
-/// Temporary(?) wrapper around RefCell<AppContext> to help us debug any double borrows.
-/// Strongly consider removing after stabilization.
-pub struct AppCell {
-    app: RefCell<AppContext>,
-}
-
-impl AppCell {
-    #[track_caller]
-    pub fn borrow(&self) -> AppRef {
-        if option_env!("TRACK_THREAD_BORROWS").is_some() {
-            let thread_id = std::thread::current().id();
-            eprintln!("borrowed {thread_id:?}");
-        }
-        AppRef(self.app.borrow())
-    }
-
-    #[track_caller]
-    pub fn borrow_mut(&self) -> AppRefMut {
-        if option_env!("TRACK_THREAD_BORROWS").is_some() {
-            let thread_id = std::thread::current().id();
-            eprintln!("borrowed {thread_id:?}");
-        }
-        AppRefMut(self.app.borrow_mut())
-    }
-}
-
-#[derive(Deref, DerefMut)]
-pub struct AppRef<'a>(Ref<'a, AppContext>);
-
-impl<'a> Drop for AppRef<'a> {
-    fn drop(&mut self) {
-        if option_env!("TRACK_THREAD_BORROWS").is_some() {
-            let thread_id = std::thread::current().id();
-            eprintln!("dropped borrow from {thread_id:?}");
-        }
-    }
-}
-
-#[derive(Deref, DerefMut)]
-pub struct AppRefMut<'a>(RefMut<'a, AppContext>);
-
-impl<'a> Drop for AppRefMut<'a> {
-    fn drop(&mut self) {
-        if option_env!("TRACK_THREAD_BORROWS").is_some() {
-            let thread_id = std::thread::current().id();
-            eprintln!("dropped {thread_id:?}");
-        }
-    }
-}
-
-pub struct App(Rc<AppCell>);
-
-/// Represents an application before it is fully launched. Once your app is
-/// configured, you'll start the app with `App::run`.
-impl App {
-    /// Builds an app with the given asset source.
-    pub fn production(asset_source: Arc<dyn AssetSource>) -> Self {
-        Self(AppContext::new(
-            current_platform(),
-            asset_source,
-            http::client(),
-        ))
-    }
-
-    /// Start the application. The provided callback will be called once the
-    /// app is fully launched.
-    pub fn run<F>(self, on_finish_launching: F)
-    where
-        F: 'static + FnOnce(&mut AppContext),
-    {
-        let this = self.0.clone();
-        let platform = self.0.borrow().platform.clone();
-        platform.run(Box::new(move || {
-            let cx = &mut *this.borrow_mut();
-            on_finish_launching(cx);
-        }));
-    }
-
-    /// Register a handler to be invoked when the platform instructs the application
-    /// to open one or more URLs.
-    pub fn on_open_urls<F>(&self, mut callback: F) -> &Self
-    where
-        F: 'static + FnMut(Vec<String>, &mut AppContext),
-    {
-        let this = Rc::downgrade(&self.0);
-        self.0.borrow().platform.on_open_urls(Box::new(move |urls| {
-            if let Some(app) = this.upgrade() {
-                callback(urls, &mut app.borrow_mut());
-            }
-        }));
-        self
-    }
-
-    pub fn on_reopen<F>(&self, mut callback: F) -> &Self
-    where
-        F: 'static + FnMut(&mut AppContext),
-    {
-        let this = Rc::downgrade(&self.0);
-        self.0.borrow_mut().platform.on_reopen(Box::new(move || {
-            if let Some(app) = this.upgrade() {
-                callback(&mut app.borrow_mut());
-            }
-        }));
-        self
-    }
-
-    pub fn metadata(&self) -> AppMetadata {
-        self.0.borrow().app_metadata.clone()
-    }
-
-    pub fn background_executor(&self) -> BackgroundExecutor {
-        self.0.borrow().background_executor.clone()
-    }
-
-    pub fn foreground_executor(&self) -> ForegroundExecutor {
-        self.0.borrow().foreground_executor.clone()
-    }
-
-    pub fn text_system(&self) -> Arc<TextSystem> {
-        self.0.borrow().text_system.clone()
-    }
-}
-
-pub(crate) type FrameCallback = Box<dyn FnOnce(&mut AppContext)>;
-type Handler = Box<dyn FnMut(&mut AppContext) -> bool + 'static>;
-type Listener = Box<dyn FnMut(&dyn Any, &mut AppContext) -> bool + 'static>;
-type KeystrokeObserver = Box<dyn FnMut(&KeystrokeEvent, &mut WindowContext) + 'static>;
-type QuitHandler = Box<dyn FnOnce(&mut AppContext) -> LocalBoxFuture<'static, ()> + 'static>;
-type ReleaseListener = Box<dyn FnOnce(&mut dyn Any, &mut AppContext) + 'static>;
-type NewViewListener = Box<dyn FnMut(AnyView, &mut WindowContext) + 'static>;
-
-// struct FrameConsumer {
-//     next_frame_callbacks: Vec<FrameCallback>,
-//     task: Task<()>,
-//     display_linker
-// }
-
-pub struct AppContext {
-    pub(crate) this: Weak<AppCell>,
-    pub(crate) platform: Rc<dyn Platform>,
-    app_metadata: AppMetadata,
-    text_system: Arc<TextSystem>,
-    flushing_effects: bool,
-    pending_updates: usize,
-    pub(crate) actions: Rc<ActionRegistry>,
-    pub(crate) active_drag: Option<AnyDrag>,
-    pub(crate) active_tooltip: Option<AnyTooltip>,
-    pub(crate) next_frame_callbacks: FxHashMap<DisplayId, Vec<FrameCallback>>,
-    pub(crate) frame_consumers: FxHashMap<DisplayId, Task<()>>,
-    pub(crate) background_executor: BackgroundExecutor,
-    pub(crate) foreground_executor: ForegroundExecutor,
-    pub(crate) svg_renderer: SvgRenderer,
-    asset_source: Arc<dyn AssetSource>,
-    pub(crate) image_cache: ImageCache,
-    pub(crate) text_style_stack: Vec<TextStyleRefinement>,
-    pub(crate) globals_by_type: FxHashMap<TypeId, Box<dyn Any>>,
-    pub(crate) entities: EntityMap,
-    pub(crate) new_view_observers: SubscriberSet<TypeId, NewViewListener>,
-    pub(crate) windows: SlotMap<WindowId, Option<Window>>,
-    pub(crate) keymap: Arc<Mutex<Keymap>>,
-    pub(crate) global_action_listeners:
-        FxHashMap<TypeId, Vec<Rc<dyn Fn(&dyn Any, DispatchPhase, &mut Self)>>>,
-    pending_effects: VecDeque<Effect>,
-    pub(crate) pending_notifications: FxHashSet<EntityId>,
-    pub(crate) pending_global_notifications: FxHashSet<TypeId>,
-    pub(crate) observers: SubscriberSet<EntityId, Handler>,
-    // TypeId is the type of the event that the listener callback expects
-    pub(crate) event_listeners: SubscriberSet<EntityId, (TypeId, Listener)>,
-    pub(crate) keystroke_observers: SubscriberSet<(), KeystrokeObserver>,
-    pub(crate) release_listeners: SubscriberSet<EntityId, ReleaseListener>,
-    pub(crate) global_observers: SubscriberSet<TypeId, Handler>,
-    pub(crate) quit_observers: SubscriberSet<(), QuitHandler>,
-    pub(crate) layout_id_buffer: Vec<LayoutId>, // We recycle this memory across layout requests.
-    pub(crate) propagate_event: bool,
-}
-
-impl AppContext {
-    pub(crate) fn new(
-        platform: Rc<dyn Platform>,
-        asset_source: Arc<dyn AssetSource>,
-        http_client: Arc<dyn HttpClient>,
-    ) -> Rc<AppCell> {
-        let executor = platform.background_executor();
-        let foreground_executor = platform.foreground_executor();
-        assert!(
-            executor.is_main_thread(),
-            "must construct App on main thread"
-        );
-
-        let text_system = Arc::new(TextSystem::new(platform.text_system()));
-        let entities = EntityMap::new();
-
-        let app_metadata = AppMetadata {
-            os_name: platform.os_name(),
-            os_version: platform.os_version().ok(),
-            app_version: platform.app_version().ok(),
-        };
-
-        let app = Rc::new_cyclic(|this| AppCell {
-            app: RefCell::new(AppContext {
-                this: this.clone(),
-                platform: platform.clone(),
-                app_metadata,
-                text_system,
-                actions: Rc::new(ActionRegistry::default()),
-                flushing_effects: false,
-                pending_updates: 0,
-                active_drag: None,
-                active_tooltip: None,
-                next_frame_callbacks: FxHashMap::default(),
-                frame_consumers: FxHashMap::default(),
-                background_executor: executor,
-                foreground_executor,
-                svg_renderer: SvgRenderer::new(asset_source.clone()),
-                asset_source,
-                image_cache: ImageCache::new(http_client),
-                text_style_stack: Vec::new(),
-                globals_by_type: FxHashMap::default(),
-                entities,
-                new_view_observers: SubscriberSet::new(),
-                windows: SlotMap::with_key(),
-                keymap: Arc::new(Mutex::new(Keymap::default())),
-                global_action_listeners: FxHashMap::default(),
-                pending_effects: VecDeque::new(),
-                pending_notifications: FxHashSet::default(),
-                pending_global_notifications: FxHashSet::default(),
-                observers: SubscriberSet::new(),
-                event_listeners: SubscriberSet::new(),
-                release_listeners: SubscriberSet::new(),
-                keystroke_observers: SubscriberSet::new(),
-                global_observers: SubscriberSet::new(),
-                quit_observers: SubscriberSet::new(),
-                layout_id_buffer: Default::default(),
-                propagate_event: true,
-            }),
-        });
-
-        init_app_menus(platform.as_ref(), &mut app.borrow_mut());
-
-        platform.on_quit(Box::new({
-            let cx = app.clone();
-            move || {
-                cx.borrow_mut().shutdown();
-            }
-        }));
-
-        app
-    }
-
-    /// Quit the application gracefully. Handlers registered with `ModelContext::on_app_quit`
-    /// will be given 100ms to complete before exiting.
-    pub fn shutdown(&mut self) {
-        let mut futures = Vec::new();
-
-        for observer in self.quit_observers.remove(&()) {
-            futures.push(observer(self));
-        }
-
-        self.windows.clear();
-        self.flush_effects();
-
-        let futures = futures::future::join_all(futures);
-        if self
-            .background_executor
-            .block_with_timeout(Duration::from_millis(100), futures)
-            .is_err()
-        {
-            log::error!("timed out waiting on app_will_quit");
-        }
-    }
-
-    pub fn quit(&mut self) {
-        self.platform.quit();
-    }
-
-    pub fn app_metadata(&self) -> AppMetadata {
-        self.app_metadata.clone()
-    }
-
-    /// Schedules all windows in the application to be redrawn. This can be called
-    /// multiple times in an update cycle and still result in a single redraw.
-    pub fn refresh(&mut self) {
-        self.pending_effects.push_back(Effect::Refresh);
-    }
-    pub(crate) fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
-        self.pending_updates += 1;
-        let result = update(self);
-        if !self.flushing_effects && self.pending_updates == 1 {
-            self.flushing_effects = true;
-            self.flush_effects();
-            self.flushing_effects = false;
-        }
-        self.pending_updates -= 1;
-        result
-    }
-
-    pub fn observe<W, E>(
-        &mut self,
-        entity: &E,
-        mut on_notify: impl FnMut(E, &mut AppContext) + 'static,
-    ) -> Subscription
-    where
-        W: 'static,
-        E: Entity<W>,
-    {
-        self.observe_internal(entity, move |e, cx| {
-            on_notify(e, cx);
-            true
-        })
-    }
-
-    pub fn observe_internal<W, E>(
-        &mut self,
-        entity: &E,
-        mut on_notify: impl FnMut(E, &mut AppContext) -> bool + 'static,
-    ) -> Subscription
-    where
-        W: 'static,
-        E: Entity<W>,
-    {
-        let entity_id = entity.entity_id();
-        let handle = entity.downgrade();
-        let (subscription, activate) = self.observers.insert(
-            entity_id,
-            Box::new(move |cx| {
-                if let Some(handle) = E::upgrade_from(&handle) {
-                    on_notify(handle, cx)
-                } else {
-                    false
-                }
-            }),
-        );
-        self.defer(move |_| activate());
-        subscription
-    }
-
-    pub fn subscribe<T, E, Evt>(
-        &mut self,
-        entity: &E,
-        mut on_event: impl FnMut(E, &Evt, &mut AppContext) + 'static,
-    ) -> Subscription
-    where
-        T: 'static + EventEmitter<Evt>,
-        E: Entity<T>,
-        Evt: 'static,
-    {
-        self.subscribe_internal(entity, move |entity, event, cx| {
-            on_event(entity, event, cx);
-            true
-        })
-    }
-
-    pub(crate) fn subscribe_internal<T, E, Evt>(
-        &mut self,
-        entity: &E,
-        mut on_event: impl FnMut(E, &Evt, &mut AppContext) -> bool + 'static,
-    ) -> Subscription
-    where
-        T: 'static + EventEmitter<Evt>,
-        E: Entity<T>,
-        Evt: 'static,
-    {
-        let entity_id = entity.entity_id();
-        let entity = entity.downgrade();
-        let (subscription, activate) = self.event_listeners.insert(
-            entity_id,
-            (
-                TypeId::of::<Evt>(),
-                Box::new(move |event, cx| {
-                    let event: &Evt = event.downcast_ref().expect("invalid event type");
-                    if let Some(handle) = E::upgrade_from(&entity) {
-                        on_event(handle, event, cx)
-                    } else {
-                        false
-                    }
-                }),
-            ),
-        );
-        self.defer(move |_| activate());
-        subscription
-    }
-
-    pub fn windows(&self) -> Vec<AnyWindowHandle> {
-        self.windows
-            .values()
-            .filter_map(|window| Some(window.as_ref()?.handle))
-            .collect()
-    }
-
-    pub fn active_window(&self) -> Option<AnyWindowHandle> {
-        self.platform.active_window()
-    }
-
-    /// Opens a new window with the given option and the root view returned by the given function.
-    /// The function is invoked with a `WindowContext`, which can be used to interact with window-specific
-    /// functionality.
-    pub fn open_window<V: 'static + Render>(
-        &mut self,
-        options: crate::WindowOptions,
-        build_root_view: impl FnOnce(&mut WindowContext) -> View<V>,
-    ) -> WindowHandle<V> {
-        self.update(|cx| {
-            let id = cx.windows.insert(None);
-            let handle = WindowHandle::new(id);
-            let mut window = Window::new(handle.into(), options, cx);
-            let root_view = build_root_view(&mut WindowContext::new(cx, &mut window));
-            window.root_view.replace(root_view.into());
-            cx.windows.get_mut(id).unwrap().replace(window);
-            handle
-        })
-    }
-
-    /// Instructs the platform to activate the application by bringing it to the foreground.
-    pub fn activate(&self, ignoring_other_apps: bool) {
-        self.platform.activate(ignoring_other_apps);
-    }
-
-    pub fn hide(&self) {
-        self.platform.hide();
-    }
-
-    pub fn hide_other_apps(&self) {
-        self.platform.hide_other_apps();
-    }
-
-    pub fn unhide_other_apps(&self) {
-        self.platform.unhide_other_apps();
-    }
-
-    /// Returns the list of currently active displays.
-    pub fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
-        self.platform.displays()
-    }
-
-    /// Writes data to the platform clipboard.
-    pub fn write_to_clipboard(&self, item: ClipboardItem) {
-        self.platform.write_to_clipboard(item)
-    }
-
-    /// Reads data from the platform clipboard.
-    pub fn read_from_clipboard(&self) -> Option<ClipboardItem> {
-        self.platform.read_from_clipboard()
-    }
-
-    /// Writes credentials to the platform keychain.
-    pub fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Result<()> {
-        self.platform.write_credentials(url, username, password)
-    }
-
-    /// Reads credentials from the platform keychain.
-    pub fn read_credentials(&self, url: &str) -> Result<Option<(String, Vec<u8>)>> {
-        self.platform.read_credentials(url)
-    }
-
-    /// Deletes credentials from the platform keychain.
-    pub fn delete_credentials(&self, url: &str) -> Result<()> {
-        self.platform.delete_credentials(url)
-    }
-
-    /// Directs the platform's default browser to open the given URL.
-    pub fn open_url(&self, url: &str) {
-        self.platform.open_url(url);
-    }
-
-    pub fn app_path(&self) -> Result<PathBuf> {
-        self.platform.app_path()
-    }
-
-    pub fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
-        self.platform.path_for_auxiliary_executable(name)
-    }
-
-    pub fn double_click_interval(&self) -> Duration {
-        self.platform.double_click_interval()
-    }
-
-    pub fn prompt_for_paths(
-        &self,
-        options: PathPromptOptions,
-    ) -> oneshot::Receiver<Option<Vec<PathBuf>>> {
-        self.platform.prompt_for_paths(options)
-    }
-
-    pub fn prompt_for_new_path(&self, directory: &Path) -> oneshot::Receiver<Option<PathBuf>> {
-        self.platform.prompt_for_new_path(directory)
-    }
-
-    pub fn reveal_path(&self, path: &Path) {
-        self.platform.reveal_path(path)
-    }
-
-    pub fn should_auto_hide_scrollbars(&self) -> bool {
-        self.platform.should_auto_hide_scrollbars()
-    }
-
-    pub fn restart(&self) {
-        self.platform.restart()
-    }
-
-    pub fn local_timezone(&self) -> UtcOffset {
-        self.platform.local_timezone()
-    }
-
-    pub(crate) fn push_effect(&mut self, effect: Effect) {
-        match &effect {
-            Effect::Notify { emitter } => {
-                if !self.pending_notifications.insert(*emitter) {
-                    return;
-                }
-            }
-            Effect::NotifyGlobalObservers { global_type } => {
-                if !self.pending_global_notifications.insert(*global_type) {
-                    return;
-                }
-            }
-            _ => {}
-        };
-
-        self.pending_effects.push_back(effect);
-    }
-
-    /// Called at the end of AppContext::update to complete any side effects
-    /// such as notifying observers, emitting events, etc. Effects can themselves
-    /// cause effects, so we continue looping until all effects are processed.
-    fn flush_effects(&mut self) {
-        loop {
-            self.release_dropped_entities();
-            self.release_dropped_focus_handles();
-
-            if let Some(effect) = self.pending_effects.pop_front() {
-                match effect {
-                    Effect::Notify { emitter } => {
-                        self.apply_notify_effect(emitter);
-                    }
-
-                    Effect::Emit {
-                        emitter,
-                        event_type,
-                        event,
-                    } => self.apply_emit_effect(emitter, event_type, event),
-
-                    Effect::Refresh => {
-                        self.apply_refresh_effect();
-                    }
-
-                    Effect::NotifyGlobalObservers { global_type } => {
-                        self.apply_notify_global_observers_effect(global_type);
-                    }
-
-                    Effect::Defer { callback } => {
-                        self.apply_defer_effect(callback);
-                    }
-                }
-            } else {
-                for window in self.windows.values() {
-                    if let Some(window) = window.as_ref() {
-                        if window.dirty {
-                            window.platform_window.invalidate();
-                        }
-                    }
-                }
-
-                #[cfg(any(test, feature = "test-support"))]
-                for window in self
-                    .windows
-                    .values()
-                    .filter_map(|window| {
-                        let window = window.as_ref()?;
-                        (window.dirty || window.focus_invalidated).then_some(window.handle)
-                    })
-                    .collect::<Vec<_>>()
-                {
-                    self.update_window(window, |_, cx| cx.draw()).unwrap();
-                }
-
-                if self.pending_effects.is_empty() {
-                    break;
-                }
-            }
-        }
-    }
-
-    /// Repeatedly called during `flush_effects` to release any entities whose
-    /// reference count has become zero. We invoke any release observers before dropping
-    /// each entity.
-    fn release_dropped_entities(&mut self) {
-        loop {
-            let dropped = self.entities.take_dropped();
-            if dropped.is_empty() {
-                break;
-            }
-
-            for (entity_id, mut entity) in dropped {
-                self.observers.remove(&entity_id);
-                self.event_listeners.remove(&entity_id);
-                for release_callback in self.release_listeners.remove(&entity_id) {
-                    release_callback(entity.as_mut(), self);
-                }
-            }
-        }
-    }
-
-    /// Repeatedly called during `flush_effects` to handle a focused handle being dropped.
-    fn release_dropped_focus_handles(&mut self) {
-        for window_handle in self.windows() {
-            window_handle
-                .update(self, |_, cx| {
-                    let mut blur_window = false;
-                    let focus = cx.window.focus;
-                    cx.window.focus_handles.write().retain(|handle_id, count| {
-                        if count.load(SeqCst) == 0 {
-                            if focus == Some(handle_id) {
-                                blur_window = true;
-                            }
-                            false
-                        } else {
-                            true
-                        }
-                    });
-
-                    if blur_window {
-                        cx.blur();
-                    }
-                })
-                .unwrap();
-        }
-    }
-
-    fn apply_notify_effect(&mut self, emitter: EntityId) {
-        self.pending_notifications.remove(&emitter);
-
-        self.observers
-            .clone()
-            .retain(&emitter, |handler| handler(self));
-    }
-
-    fn apply_emit_effect(&mut self, emitter: EntityId, event_type: TypeId, event: Box<dyn Any>) {
-        self.event_listeners
-            .clone()
-            .retain(&emitter, |(stored_type, handler)| {
-                if *stored_type == event_type {
-                    handler(event.as_ref(), self)
-                } else {
-                    true
-                }
-            });
-    }
-
-    fn apply_refresh_effect(&mut self) {
-        for window in self.windows.values_mut() {
-            if let Some(window) = window.as_mut() {
-                window.dirty = true;
-            }
-        }
-    }
-
-    fn apply_notify_global_observers_effect(&mut self, type_id: TypeId) {
-        self.pending_global_notifications.remove(&type_id);
-        self.global_observers
-            .clone()
-            .retain(&type_id, |observer| observer(self));
-    }
-
-    fn apply_defer_effect(&mut self, callback: Box<dyn FnOnce(&mut Self) + 'static>) {
-        callback(self);
-    }
-
-    /// Creates an `AsyncAppContext`, which can be cloned and has a static lifetime
-    /// so it can be held across `await` points.
-    pub fn to_async(&self) -> AsyncAppContext {
-        AsyncAppContext {
-            app: unsafe { mem::transmute(self.this.clone()) },
-            background_executor: self.background_executor.clone(),
-            foreground_executor: self.foreground_executor.clone(),
-        }
-    }
-
-    /// Obtains a reference to the executor, which can be used to spawn futures.
-    pub fn background_executor(&self) -> &BackgroundExecutor {
-        &self.background_executor
-    }
-
-    /// Obtains a reference to the executor, which can be used to spawn futures.
-    pub fn foreground_executor(&self) -> &ForegroundExecutor {
-        &self.foreground_executor
-    }
-
-    /// Spawns the future returned by the given function on the thread pool. The closure will be invoked
-    /// with AsyncAppContext, which allows the application state to be accessed across await points.
-    pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task<R>
-    where
-        Fut: Future<Output = R> + 'static,
-        R: 'static,
-    {
-        self.foreground_executor.spawn(f(self.to_async()))
-    }
-
-    /// Schedules the given function to be run at the end of the current effect cycle, allowing entities
-    /// that are currently on the stack to be returned to the app.
-    pub fn defer(&mut self, f: impl FnOnce(&mut AppContext) + 'static) {
-        self.push_effect(Effect::Defer {
-            callback: Box::new(f),
-        });
-    }
-
-    /// Accessor for the application's asset source, which is provided when constructing the `App`.
-    pub fn asset_source(&self) -> &Arc<dyn AssetSource> {
-        &self.asset_source
-    }
-
-    /// Accessor for the text system.
-    pub fn text_system(&self) -> &Arc<TextSystem> {
-        &self.text_system
-    }
-
-    /// The current text style. Which is composed of all the style refinements provided to `with_text_style`.
-    pub fn text_style(&self) -> TextStyle {
-        let mut style = TextStyle::default();
-        for refinement in &self.text_style_stack {
-            style.refine(refinement);
-        }
-        style
-    }
-
-    /// Check whether a global of the given type has been assigned.
-    pub fn has_global<G: 'static>(&self) -> bool {
-        self.globals_by_type.contains_key(&TypeId::of::<G>())
-    }
-
-    /// Access the global of the given type. Panics if a global for that type has not been assigned.
-    #[track_caller]
-    pub fn global<G: 'static>(&self) -> &G {
-        self.globals_by_type
-            .get(&TypeId::of::<G>())
-            .map(|any_state| any_state.downcast_ref::<G>().unwrap())
-            .ok_or_else(|| anyhow!("no state of type {} exists", type_name::<G>()))
-            .unwrap()
-    }
-
-    /// Access the global of the given type if a value has been assigned.
-    pub fn try_global<G: 'static>(&self) -> Option<&G> {
-        self.globals_by_type
-            .get(&TypeId::of::<G>())
-            .map(|any_state| any_state.downcast_ref::<G>().unwrap())
-    }
-
-    /// Access the global of the given type mutably. Panics if a global for that type has not been assigned.
-    #[track_caller]
-    pub fn global_mut<G: 'static>(&mut self) -> &mut G {
-        let global_type = TypeId::of::<G>();
-        self.push_effect(Effect::NotifyGlobalObservers { global_type });
-        self.globals_by_type
-            .get_mut(&global_type)
-            .and_then(|any_state| any_state.downcast_mut::<G>())
-            .ok_or_else(|| anyhow!("no state of type {} exists", type_name::<G>()))
-            .unwrap()
-    }
-
-    /// Access the global of the given type mutably. A default value is assigned if a global of this type has not
-    /// yet been assigned.
-    pub fn default_global<G: 'static + Default>(&mut self) -> &mut G {
-        let global_type = TypeId::of::<G>();
-        self.push_effect(Effect::NotifyGlobalObservers { global_type });
-        self.globals_by_type
-            .entry(global_type)
-            .or_insert_with(|| Box::<G>::default())
-            .downcast_mut::<G>()
-            .unwrap()
-    }
-
-    /// Set the value of the global of the given type.
-    pub fn set_global<G: Any>(&mut self, global: G) {
-        let global_type = TypeId::of::<G>();
-        self.push_effect(Effect::NotifyGlobalObservers { global_type });
-        self.globals_by_type.insert(global_type, Box::new(global));
-    }
-
-    /// Clear all stored globals. Does not notify global observers.
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn clear_globals(&mut self) {
-        self.globals_by_type.drain();
-    }
-
-    /// Remove the global of the given type from the app context. Does not notify global observers.
-    pub fn remove_global<G: Any>(&mut self) -> G {
-        let global_type = TypeId::of::<G>();
-        *self
-            .globals_by_type
-            .remove(&global_type)
-            .unwrap_or_else(|| panic!("no global added for {}", std::any::type_name::<G>()))
-            .downcast()
-            .unwrap()
-    }
-
-    /// Update the global of the given type with a closure. Unlike `global_mut`, this method provides
-    /// your closure with mutable access to the `AppContext` and the global simultaneously.
-    pub fn update_global<G: 'static, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R {
-        let mut global = self.lease_global::<G>();
-        let result = f(&mut global, self);
-        self.end_global_lease(global);
-        result
-    }
-
-    /// Register a callback to be invoked when a global of the given type is updated.
-    pub fn observe_global<G: 'static>(
-        &mut self,
-        mut f: impl FnMut(&mut Self) + 'static,
-    ) -> Subscription {
-        let (subscription, activate) = self.global_observers.insert(
-            TypeId::of::<G>(),
-            Box::new(move |cx| {
-                f(cx);
-                true
-            }),
-        );
-        self.defer(move |_| activate());
-        subscription
-    }
-
-    /// Move the global of the given type to the stack.
-    pub(crate) fn lease_global<G: 'static>(&mut self) -> GlobalLease<G> {
-        GlobalLease::new(
-            self.globals_by_type
-                .remove(&TypeId::of::<G>())
-                .ok_or_else(|| anyhow!("no global registered of type {}", type_name::<G>()))
-                .unwrap(),
-        )
-    }
-
-    /// Restore the global of the given type after it is moved to the stack.
-    pub(crate) fn end_global_lease<G: 'static>(&mut self, lease: GlobalLease<G>) {
-        let global_type = TypeId::of::<G>();
-        self.push_effect(Effect::NotifyGlobalObservers { global_type });
-        self.globals_by_type.insert(global_type, lease.global);
-    }
-
-    pub fn observe_new_views<V: 'static>(
-        &mut self,
-        on_new: impl 'static + Fn(&mut V, &mut ViewContext<V>),
-    ) -> Subscription {
-        let (subscription, activate) = self.new_view_observers.insert(
-            TypeId::of::<V>(),
-            Box::new(move |any_view: AnyView, cx: &mut WindowContext| {
-                any_view
-                    .downcast::<V>()
-                    .unwrap()
-                    .update(cx, |view_state, cx| {
-                        on_new(view_state, cx);
-                    })
-            }),
-        );
-        activate();
-        subscription
-    }
-
-    pub fn observe_release<E, T>(
-        &mut self,
-        handle: &E,
-        on_release: impl FnOnce(&mut T, &mut AppContext) + 'static,
-    ) -> Subscription
-    where
-        E: Entity<T>,
-        T: 'static,
-    {
-        let (subscription, activate) = self.release_listeners.insert(
-            handle.entity_id(),
-            Box::new(move |entity, cx| {
-                let entity = entity.downcast_mut().expect("invalid entity type");
-                on_release(entity, cx)
-            }),
-        );
-        activate();
-        subscription
-    }
-
-    pub fn observe_keystrokes(
-        &mut self,
-        f: impl FnMut(&KeystrokeEvent, &mut WindowContext) + 'static,
-    ) -> Subscription {
-        let (subscription, activate) = self.keystroke_observers.insert((), Box::new(f));
-        activate();
-        subscription
-    }
-
-    pub(crate) fn push_text_style(&mut self, text_style: TextStyleRefinement) {
-        self.text_style_stack.push(text_style);
-    }
-
-    pub(crate) fn pop_text_style(&mut self) {
-        self.text_style_stack.pop();
-    }
-
-    /// Register key bindings.
-    pub fn bind_keys(&mut self, bindings: impl IntoIterator<Item = KeyBinding>) {
-        self.keymap.lock().add_bindings(bindings);
-        self.pending_effects.push_back(Effect::Refresh);
-    }
-
-    /// Register a global listener for actions invoked via the keyboard.
-    pub fn on_action<A: Action>(&mut self, listener: impl Fn(&A, &mut Self) + 'static) {
-        self.global_action_listeners
-            .entry(TypeId::of::<A>())
-            .or_default()
-            .push(Rc::new(move |action, phase, cx| {
-                if phase == DispatchPhase::Bubble {
-                    let action = action.downcast_ref().unwrap();
-                    listener(action, cx)
-                }
-            }));
-    }
-
-    /// Event handlers propagate events by default. Call this method to stop dispatching to
-    /// event handlers with a lower z-index (mouse) or higher in the tree (keyboard). This is
-    /// the opposite of [propagate]. It's also possible to cancel a call to [propagate] by
-    /// calling this method before effects are flushed.
-    pub fn stop_propagation(&mut self) {
-        self.propagate_event = false;
-    }
-
-    /// Action handlers stop propagation by default during the bubble phase of action dispatch
-    /// dispatching to action handlers higher in the element tree. This is the opposite of
-    /// [stop_propagation]. It's also possible to cancel a call to [stop_propagate] by calling
-    /// this method before effects are flushed.
-    pub fn propagate(&mut self) {
-        self.propagate_event = true;
-    }
-
-    pub fn build_action(
-        &self,
-        name: &str,
-        data: Option<serde_json::Value>,
-    ) -> Result<Box<dyn Action>> {
-        self.actions.build_action(name, data)
-    }
-
-    pub fn all_action_names(&self) -> &[SharedString] {
-        self.actions.all_action_names()
-    }
-
-    pub fn on_app_quit<Fut>(
-        &mut self,
-        mut on_quit: impl FnMut(&mut AppContext) -> Fut + 'static,
-    ) -> Subscription
-    where
-        Fut: 'static + Future<Output = ()>,
-    {
-        let (subscription, activate) = self.quit_observers.insert(
-            (),
-            Box::new(move |cx| {
-                let future = on_quit(cx);
-                future.boxed_local()
-            }),
-        );
-        activate();
-        subscription
-    }
-
-    pub(crate) fn clear_pending_keystrokes(&mut self) {
-        for window in self.windows() {
-            window
-                .update(self, |_, cx| {
-                    cx.window
-                        .rendered_frame
-                        .dispatch_tree
-                        .clear_pending_keystrokes();
-                    cx.window
-                        .next_frame
-                        .dispatch_tree
-                        .clear_pending_keystrokes();
-                })
-                .ok();
-        }
-    }
-
-    pub fn is_action_available(&mut self, action: &dyn Action) -> bool {
-        if let Some(window) = self.active_window() {
-            if let Ok(window_action_available) =
-                window.update(self, |_, cx| cx.is_action_available(action))
-            {
-                return window_action_available;
-            }
-        }
-
-        self.global_action_listeners
-            .contains_key(&action.as_any().type_id())
-    }
-
-    pub fn set_menus(&mut self, menus: Vec<Menu>) {
-        self.platform.set_menus(menus, &self.keymap.lock());
-    }
-
-    pub fn dispatch_action(&mut self, action: &dyn Action) {
-        if let Some(active_window) = self.active_window() {
-            active_window
-                .update(self, |_, cx| cx.dispatch_action(action.boxed_clone()))
-                .log_err();
-        } else {
-            self.propagate_event = true;
-
-            if let Some(mut global_listeners) = self
-                .global_action_listeners
-                .remove(&action.as_any().type_id())
-            {
-                for listener in &global_listeners {
-                    listener(action.as_any(), DispatchPhase::Capture, self);
-                    if !self.propagate_event {
-                        break;
-                    }
-                }
-
-                global_listeners.extend(
-                    self.global_action_listeners
-                        .remove(&action.as_any().type_id())
-                        .unwrap_or_default(),
-                );
-
-                self.global_action_listeners
-                    .insert(action.as_any().type_id(), global_listeners);
-            }
-
-            if self.propagate_event {
-                if let Some(mut global_listeners) = self
-                    .global_action_listeners
-                    .remove(&action.as_any().type_id())
-                {
-                    for listener in global_listeners.iter().rev() {
-                        listener(action.as_any(), DispatchPhase::Bubble, self);
-                        if !self.propagate_event {
-                            break;
-                        }
-                    }
-
-                    global_listeners.extend(
-                        self.global_action_listeners
-                            .remove(&action.as_any().type_id())
-                            .unwrap_or_default(),
-                    );
-
-                    self.global_action_listeners
-                        .insert(action.as_any().type_id(), global_listeners);
-                }
-            }
-        }
-    }
-
-    pub fn has_active_drag(&self) -> bool {
-        self.active_drag.is_some()
-    }
-
-    pub fn active_drag<T: 'static>(&self) -> Option<&T> {
-        self.active_drag
-            .as_ref()
-            .and_then(|drag| drag.value.downcast_ref())
-    }
-}
-
-impl Context for AppContext {
-    type Result<T> = T;
-
-    /// Build an entity that is owned by the application. The given function will be invoked with
-    /// a `ModelContext` and must return an object representing the entity. A `Model` will be returned
-    /// which can be used to access the entity in a context.
-    fn new_model<T: 'static>(
-        &mut self,
-        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
-    ) -> Model<T> {
-        self.update(|cx| {
-            let slot = cx.entities.reserve();
-            let entity = build_model(&mut ModelContext::new(cx, slot.downgrade()));
-            cx.entities.insert(slot, entity)
-        })
-    }
-
-    /// Update the entity referenced by the given model. The function is passed a mutable reference to the
-    /// entity along with a `ModelContext` for the entity.
-    fn update_model<T: 'static, R>(
-        &mut self,
-        model: &Model<T>,
-        update: impl FnOnce(&mut T, &mut ModelContext<'_, T>) -> R,
-    ) -> R {
-        self.update(|cx| {
-            let mut entity = cx.entities.lease(model);
-            let result = update(&mut entity, &mut ModelContext::new(cx, model.downgrade()));
-            cx.entities.end_lease(entity);
-            result
-        })
-    }
-
-    fn update_window<T, F>(&mut self, handle: AnyWindowHandle, update: F) -> Result<T>
-    where
-        F: FnOnce(AnyView, &mut WindowContext<'_>) -> T,
-    {
-        self.update(|cx| {
-            let mut window = cx
-                .windows
-                .get_mut(handle.id)
-                .ok_or_else(|| anyhow!("window not found"))?
-                .take()
-                .unwrap();
-
-            let root_view = window.root_view.clone().unwrap();
-            let result = update(root_view, &mut WindowContext::new(cx, &mut window));
-
-            if window.removed {
-                cx.windows.remove(handle.id);
-            } else {
-                cx.windows
-                    .get_mut(handle.id)
-                    .ok_or_else(|| anyhow!("window not found"))?
-                    .replace(window);
-            }
-
-            Ok(result)
-        })
-    }
-
-    fn read_model<T, R>(
-        &self,
-        handle: &Model<T>,
-        read: impl FnOnce(&T, &AppContext) -> R,
-    ) -> Self::Result<R>
-    where
-        T: 'static,
-    {
-        let entity = self.entities.read(handle);
-        read(entity, self)
-    }
-
-    fn read_window<T, R>(
-        &self,
-        window: &WindowHandle<T>,
-        read: impl FnOnce(View<T>, &AppContext) -> R,
-    ) -> Result<R>
-    where
-        T: 'static,
-    {
-        let window = self
-            .windows
-            .get(window.id)
-            .ok_or_else(|| anyhow!("window not found"))?
-            .as_ref()
-            .unwrap();
-
-        let root_view = window.root_view.clone().unwrap();
-        let view = root_view
-            .downcast::<T>()
-            .map_err(|_| anyhow!("root view's type has changed"))?;
-
-        Ok(read(view, self))
-    }
-}
-
-/// These effects are processed at the end of each application update cycle.
-pub(crate) enum Effect {
-    Notify {
-        emitter: EntityId,
-    },
-    Emit {
-        emitter: EntityId,
-        event_type: TypeId,
-        event: Box<dyn Any>,
-    },
-    Refresh,
-    NotifyGlobalObservers {
-        global_type: TypeId,
-    },
-    Defer {
-        callback: Box<dyn FnOnce(&mut AppContext) + 'static>,
-    },
-}
-
-/// Wraps a global variable value during `update_global` while the value has been moved to the stack.
-pub(crate) struct GlobalLease<G: 'static> {
-    global: Box<dyn Any>,
-    global_type: PhantomData<G>,
-}
-
-impl<G: 'static> GlobalLease<G> {
-    fn new(global: Box<dyn Any>) -> Self {
-        GlobalLease {
-            global,
-            global_type: PhantomData,
-        }
-    }
-}
-
-impl<G: 'static> Deref for GlobalLease<G> {
-    type Target = G;
-
-    fn deref(&self) -> &Self::Target {
-        self.global.downcast_ref().unwrap()
-    }
-}
-
-impl<G: 'static> DerefMut for GlobalLease<G> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        self.global.downcast_mut().unwrap()
-    }
-}
-
-/// Contains state associated with an active drag operation, started by dragging an element
-/// within the window or by dragging into the app from the underlying platform.
-pub struct AnyDrag {
-    pub view: AnyView,
-    pub value: Box<dyn Any>,
-    pub cursor_offset: Point<Pixels>,
-}
-
-#[derive(Clone)]
-pub(crate) struct AnyTooltip {
-    pub view: AnyView,
-    pub cursor_offset: Point<Pixels>,
-}
-
-#[derive(Debug)]
-pub struct KeystrokeEvent {
-    pub keystroke: Keystroke,
-    pub action: Option<Box<dyn Action>>,
-}

crates/gpui2/src/assets.rs 🔗

@@ -1,64 +0,0 @@
-use crate::{size, DevicePixels, Result, SharedString, Size};
-use anyhow::anyhow;
-use image::{Bgra, ImageBuffer};
-use std::{
-    borrow::Cow,
-    fmt,
-    hash::Hash,
-    sync::atomic::{AtomicUsize, Ordering::SeqCst},
-};
-
-pub trait AssetSource: 'static + Send + Sync {
-    fn load(&self, path: &str) -> Result<Cow<[u8]>>;
-    fn list(&self, path: &str) -> Result<Vec<SharedString>>;
-}
-
-impl AssetSource for () {
-    fn load(&self, path: &str) -> Result<Cow<[u8]>> {
-        Err(anyhow!(
-            "get called on empty asset provider with \"{}\"",
-            path
-        ))
-    }
-
-    fn list(&self, _path: &str) -> Result<Vec<SharedString>> {
-        Ok(vec![])
-    }
-}
-
-#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
-pub struct ImageId(usize);
-
-pub struct ImageData {
-    pub id: ImageId,
-    data: ImageBuffer<Bgra<u8>, Vec<u8>>,
-}
-
-impl ImageData {
-    pub fn new(data: ImageBuffer<Bgra<u8>, Vec<u8>>) -> Self {
-        static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
-
-        Self {
-            id: ImageId(NEXT_ID.fetch_add(1, SeqCst)),
-            data,
-        }
-    }
-
-    pub fn as_bytes(&self) -> &[u8] {
-        &self.data
-    }
-
-    pub fn size(&self) -> Size<DevicePixels> {
-        let (width, height) = self.data.dimensions();
-        size(width.into(), height.into())
-    }
-}
-
-impl fmt::Debug for ImageData {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("ImageData")
-            .field("id", &self.id)
-            .field("size", &self.data.dimensions())
-            .finish()
-    }
-}

crates/gpui2/src/color.rs 🔗

@@ -1,457 +0,0 @@
-use anyhow::bail;
-use serde::de::{self, Deserialize, Deserializer, Visitor};
-use std::fmt;
-
-pub fn rgb<C: From<Rgba>>(hex: u32) -> C {
-    let r = ((hex >> 16) & 0xFF) as f32 / 255.0;
-    let g = ((hex >> 8) & 0xFF) as f32 / 255.0;
-    let b = (hex & 0xFF) as f32 / 255.0;
-    Rgba { r, g, b, a: 1.0 }.into()
-}
-
-pub fn rgba(hex: u32) -> Rgba {
-    let r = ((hex >> 24) & 0xFF) as f32 / 255.0;
-    let g = ((hex >> 16) & 0xFF) as f32 / 255.0;
-    let b = ((hex >> 8) & 0xFF) as f32 / 255.0;
-    let a = (hex & 0xFF) as f32 / 255.0;
-    Rgba { r, g, b, a }
-}
-
-#[derive(PartialEq, Clone, Copy, Default)]
-pub struct Rgba {
-    pub r: f32,
-    pub g: f32,
-    pub b: f32,
-    pub a: f32,
-}
-
-impl fmt::Debug for Rgba {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "rgba({:#010x})", u32::from(*self))
-    }
-}
-
-impl Rgba {
-    pub fn blend(&self, other: Rgba) -> Self {
-        if other.a >= 1.0 {
-            other
-        } else if other.a <= 0.0 {
-            return *self;
-        } else {
-            return Rgba {
-                r: (self.r * (1.0 - other.a)) + (other.r * other.a),
-                g: (self.g * (1.0 - other.a)) + (other.g * other.a),
-                b: (self.b * (1.0 - other.a)) + (other.b * other.a),
-                a: self.a,
-            };
-        }
-    }
-}
-
-impl From<Rgba> for u32 {
-    fn from(rgba: Rgba) -> Self {
-        let r = (rgba.r * 255.0) as u32;
-        let g = (rgba.g * 255.0) as u32;
-        let b = (rgba.b * 255.0) as u32;
-        let a = (rgba.a * 255.0) as u32;
-        (r << 24) | (g << 16) | (b << 8) | a
-    }
-}
-
-struct RgbaVisitor;
-
-impl<'de> Visitor<'de> for RgbaVisitor {
-    type Value = Rgba;
-
-    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
-        formatter.write_str("a string in the format #rrggbb or #rrggbbaa")
-    }
-
-    fn visit_str<E: de::Error>(self, value: &str) -> Result<Rgba, E> {
-        Rgba::try_from(value).map_err(E::custom)
-    }
-}
-
-impl<'de> Deserialize<'de> for Rgba {
-    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
-        deserializer.deserialize_str(RgbaVisitor)
-    }
-}
-
-impl From<Hsla> for Rgba {
-    fn from(color: Hsla) -> Self {
-        let h = color.h;
-        let s = color.s;
-        let l = color.l;
-
-        let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
-        let x = c * (1.0 - ((h * 6.0) % 2.0 - 1.0).abs());
-        let m = l - c / 2.0;
-        let cm = c + m;
-        let xm = x + m;
-
-        let (r, g, b) = match (h * 6.0).floor() as i32 {
-            0 | 6 => (cm, xm, m),
-            1 => (xm, cm, m),
-            2 => (m, cm, xm),
-            3 => (m, xm, cm),
-            4 => (xm, m, cm),
-            _ => (cm, m, xm),
-        };
-
-        Rgba {
-            r,
-            g,
-            b,
-            a: color.a,
-        }
-    }
-}
-
-impl TryFrom<&'_ str> for Rgba {
-    type Error = anyhow::Error;
-
-    fn try_from(value: &'_ str) -> Result<Self, Self::Error> {
-        const RGB: usize = "rgb".len();
-        const RGBA: usize = "rgba".len();
-        const RRGGBB: usize = "rrggbb".len();
-        const RRGGBBAA: usize = "rrggbbaa".len();
-
-        const EXPECTED_FORMATS: &str = "Expected #rgb, #rgba, #rrggbb, or #rrggbbaa";
-
-        let Some(("", hex)) = value.trim().split_once('#') else {
-            bail!("invalid RGBA hex color: '{value}'. {EXPECTED_FORMATS}");
-        };
-
-        let (r, g, b, a) = match hex.len() {
-            RGB | RGBA => {
-                let r = u8::from_str_radix(&hex[0..1], 16)?;
-                let g = u8::from_str_radix(&hex[1..2], 16)?;
-                let b = u8::from_str_radix(&hex[2..3], 16)?;
-                let a = if hex.len() == RGBA {
-                    u8::from_str_radix(&hex[3..4], 16)?
-                } else {
-                    0xf
-                };
-
-                /// Duplicates a given hex digit.
-                /// E.g., `0xf` -> `0xff`.
-                const fn duplicate(value: u8) -> u8 {
-                    value << 4 | value
-                }
-
-                (duplicate(r), duplicate(g), duplicate(b), duplicate(a))
-            }
-            RRGGBB | RRGGBBAA => {
-                let r = u8::from_str_radix(&hex[0..2], 16)?;
-                let g = u8::from_str_radix(&hex[2..4], 16)?;
-                let b = u8::from_str_radix(&hex[4..6], 16)?;
-                let a = if hex.len() == RRGGBBAA {
-                    u8::from_str_radix(&hex[6..8], 16)?
-                } else {
-                    0xff
-                };
-                (r, g, b, a)
-            }
-            _ => bail!("invalid RGBA hex color: '{value}'. {EXPECTED_FORMATS}"),
-        };
-
-        Ok(Rgba {
-            r: r as f32 / 255.,
-            g: g as f32 / 255.,
-            b: b as f32 / 255.,
-            a: a as f32 / 255.,
-        })
-    }
-}
-
-#[derive(Default, Copy, Clone, Debug)]
-#[repr(C)]
-pub struct Hsla {
-    pub h: f32,
-    pub s: f32,
-    pub l: f32,
-    pub a: f32,
-}
-
-impl PartialEq for Hsla {
-    fn eq(&self, other: &Self) -> bool {
-        self.h
-            .total_cmp(&other.h)
-            .then(self.s.total_cmp(&other.s))
-            .then(self.l.total_cmp(&other.l).then(self.a.total_cmp(&other.a)))
-            .is_eq()
-    }
-}
-
-impl PartialOrd for Hsla {
-    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-        // SAFETY: The total ordering relies on this always being Some()
-        Some(
-            self.h
-                .total_cmp(&other.h)
-                .then(self.s.total_cmp(&other.s))
-                .then(self.l.total_cmp(&other.l).then(self.a.total_cmp(&other.a))),
-        )
-    }
-}
-
-impl Ord for Hsla {
-    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
-        // SAFETY: The partial comparison is a total comparison
-        unsafe { self.partial_cmp(other).unwrap_unchecked() }
-    }
-}
-
-impl Hsla {
-    pub fn to_rgb(self) -> Rgba {
-        self.into()
-    }
-
-    pub fn red() -> Self {
-        red()
-    }
-
-    pub fn green() -> Self {
-        green()
-    }
-
-    pub fn blue() -> Self {
-        blue()
-    }
-
-    pub fn black() -> Self {
-        black()
-    }
-
-    pub fn white() -> Self {
-        white()
-    }
-
-    pub fn transparent_black() -> Self {
-        transparent_black()
-    }
-}
-
-impl Eq for Hsla {}
-
-pub fn hsla(h: f32, s: f32, l: f32, a: f32) -> Hsla {
-    Hsla {
-        h: h.clamp(0., 1.),
-        s: s.clamp(0., 1.),
-        l: l.clamp(0., 1.),
-        a: a.clamp(0., 1.),
-    }
-}
-
-pub fn black() -> Hsla {
-    Hsla {
-        h: 0.,
-        s: 0.,
-        l: 0.,
-        a: 1.,
-    }
-}
-
-pub fn transparent_black() -> Hsla {
-    Hsla {
-        h: 0.,
-        s: 0.,
-        l: 0.,
-        a: 0.,
-    }
-}
-
-pub fn white() -> Hsla {
-    Hsla {
-        h: 0.,
-        s: 0.,
-        l: 1.,
-        a: 1.,
-    }
-}
-
-pub fn red() -> Hsla {
-    Hsla {
-        h: 0.,
-        s: 1.,
-        l: 0.5,
-        a: 1.,
-    }
-}
-
-pub fn blue() -> Hsla {
-    Hsla {
-        h: 0.6,
-        s: 1.,
-        l: 0.5,
-        a: 1.,
-    }
-}
-
-pub fn green() -> Hsla {
-    Hsla {
-        h: 0.33,
-        s: 1.,
-        l: 0.5,
-        a: 1.,
-    }
-}
-
-pub fn yellow() -> Hsla {
-    Hsla {
-        h: 0.16,
-        s: 1.,
-        l: 0.5,
-        a: 1.,
-    }
-}
-
-impl Hsla {
-    /// Returns true if the HSLA color is fully transparent, false otherwise.
-    pub fn is_transparent(&self) -> bool {
-        self.a == 0.0
-    }
-
-    /// Blends `other` on top of `self` based on `other`'s alpha value. The resulting color is a combination of `self`'s and `other`'s colors.
-    ///
-    /// If `other`'s alpha value is 1.0 or greater, `other` color is fully opaque, thus `other` is returned as the output color.
-    /// If `other`'s alpha value is 0.0 or less, `other` color is fully transparent, thus `self` is returned as the output color.
-    /// Else, the output color is calculated as a blend of `self` and `other` based on their weighted alpha values.
-    ///
-    /// Assumptions:
-    /// - Alpha values are contained in the range [0, 1], with 1 as fully opaque and 0 as fully transparent.
-    /// - The relative contributions of `self` and `other` is based on `self`'s alpha value (`self.a`) and `other`'s  alpha value (`other.a`), `self` contributing `self.a * (1.0 - other.a)` and `other` contributing it's own alpha value.
-    /// - RGB color components are contained in the range [0, 1].
-    /// - If `self` and `other` colors are out of the valid range, the blend operation's output and behavior is undefined.
-    pub fn blend(self, other: Hsla) -> Hsla {
-        let alpha = other.a;
-
-        if alpha >= 1.0 {
-            other
-        } else if alpha <= 0.0 {
-            return self;
-        } else {
-            let converted_self = Rgba::from(self);
-            let converted_other = Rgba::from(other);
-            let blended_rgb = converted_self.blend(converted_other);
-            return Hsla::from(blended_rgb);
-        }
-    }
-
-    /// Fade out the color by a given factor. This factor should be between 0.0 and 1.0.
-    /// Where 0.0 will leave the color unchanged, and 1.0 will completely fade out the color.
-    pub fn fade_out(&mut self, factor: f32) {
-        self.a *= 1.0 - factor.clamp(0., 1.);
-    }
-}
-
-// impl From<Hsla> for Rgba {
-//     fn from(value: Hsla) -> Self {
-//         let h = value.h;
-//         let s = value.s;
-//         let l = value.l;
-
-//         let c = (1 - |2L - 1|) X s
-//     }
-// }
-
-impl From<Rgba> for Hsla {
-    fn from(color: Rgba) -> Self {
-        let r = color.r;
-        let g = color.g;
-        let b = color.b;
-
-        let max = r.max(g.max(b));
-        let min = r.min(g.min(b));
-        let delta = max - min;
-
-        let l = (max + min) / 2.0;
-        let s = if l == 0.0 || l == 1.0 {
-            0.0
-        } else if l < 0.5 {
-            delta / (2.0 * l)
-        } else {
-            delta / (2.0 - 2.0 * l)
-        };
-
-        let h = if delta == 0.0 {
-            0.0
-        } else if max == r {
-            ((g - b) / delta).rem_euclid(6.0) / 6.0
-        } else if max == g {
-            ((b - r) / delta + 2.0) / 6.0
-        } else {
-            ((r - g) / delta + 4.0) / 6.0
-        };
-
-        Hsla {
-            h,
-            s,
-            l,
-            a: color.a,
-        }
-    }
-}
-
-impl<'de> Deserialize<'de> for Hsla {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: Deserializer<'de>,
-    {
-        // First, deserialize it into Rgba
-        let rgba = Rgba::deserialize(deserializer)?;
-
-        // Then, use the From<Rgba> for Hsla implementation to convert it
-        Ok(Hsla::from(rgba))
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use serde_json::json;
-
-    use super::*;
-
-    #[test]
-    fn test_deserialize_three_value_hex_to_rgba() {
-        let actual: Rgba = serde_json::from_value(json!("#f09")).unwrap();
-
-        assert_eq!(actual, rgba(0xff0099ff))
-    }
-
-    #[test]
-    fn test_deserialize_four_value_hex_to_rgba() {
-        let actual: Rgba = serde_json::from_value(json!("#f09f")).unwrap();
-
-        assert_eq!(actual, rgba(0xff0099ff))
-    }
-
-    #[test]
-    fn test_deserialize_six_value_hex_to_rgba() {
-        let actual: Rgba = serde_json::from_value(json!("#ff0099")).unwrap();
-
-        assert_eq!(actual, rgba(0xff0099ff))
-    }
-
-    #[test]
-    fn test_deserialize_eight_value_hex_to_rgba() {
-        let actual: Rgba = serde_json::from_value(json!("#ff0099ff")).unwrap();
-
-        assert_eq!(actual, rgba(0xff0099ff))
-    }
-
-    #[test]
-    fn test_deserialize_eight_value_hex_with_padding_to_rgba() {
-        let actual: Rgba = serde_json::from_value(json!(" #f5f5f5ff   ")).unwrap();
-
-        assert_eq!(actual, rgba(0xf5f5f5ff))
-    }
-
-    #[test]
-    fn test_deserialize_eight_value_hex_with_mixed_case_to_rgba() {
-        let actual: Rgba = serde_json::from_value(json!("#DeAdbEeF")).unwrap();
-
-        assert_eq!(actual, rgba(0xdeadbeef))
-    }
-}

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

@@ -1,54 +0,0 @@
-use refineable::Refineable as _;
-
-use crate::{Bounds, Element, IntoElement, Pixels, Style, StyleRefinement, Styled, WindowContext};
-
-pub fn canvas(callback: impl 'static + FnOnce(&Bounds<Pixels>, &mut WindowContext)) -> Canvas {
-    Canvas {
-        paint_callback: Some(Box::new(callback)),
-        style: StyleRefinement::default(),
-    }
-}
-
-pub struct Canvas {
-    paint_callback: Option<Box<dyn FnOnce(&Bounds<Pixels>, &mut WindowContext)>>,
-    style: StyleRefinement,
-}
-
-impl IntoElement for Canvas {
-    type Element = Self;
-
-    fn element_id(&self) -> Option<crate::ElementId> {
-        None
-    }
-
-    fn into_element(self) -> Self::Element {
-        self
-    }
-}
-
-impl Element for Canvas {
-    type State = Style;
-
-    fn request_layout(
-        &mut self,
-        _: Option<Self::State>,
-        cx: &mut WindowContext,
-    ) -> (crate::LayoutId, Self::State) {
-        let mut style = Style::default();
-        style.refine(&self.style);
-        let layout_id = cx.request_layout(&style, []);
-        (layout_id, style)
-    }
-
-    fn paint(&mut self, bounds: Bounds<Pixels>, style: &mut Style, cx: &mut WindowContext) {
-        style.paint(bounds, cx, |cx| {
-            (self.paint_callback.take().unwrap())(&bounds, cx)
-        });
-    }
-}
-
-impl Styled for Canvas {
-    fn style(&mut self) -> &mut crate::StyleRefinement {
-        &mut self.style
-    }
-}

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

@@ -1,560 +0,0 @@
-use crate::{
-    point, px, AnyElement, AvailableSpace, BorrowAppContext, Bounds, DispatchPhase, Element,
-    IntoElement, Pixels, Point, ScrollWheelEvent, Size, Style, StyleRefinement, Styled,
-    WindowContext,
-};
-use collections::VecDeque;
-use refineable::Refineable as _;
-use std::{cell::RefCell, ops::Range, rc::Rc};
-use sum_tree::{Bias, SumTree};
-
-pub fn list(state: ListState) -> List {
-    List {
-        state,
-        style: StyleRefinement::default(),
-    }
-}
-
-pub struct List {
-    state: ListState,
-    style: StyleRefinement,
-}
-
-#[derive(Clone)]
-pub struct ListState(Rc<RefCell<StateInner>>);
-
-struct StateInner {
-    last_layout_bounds: Option<Bounds<Pixels>>,
-    render_item: Box<dyn FnMut(usize, &mut WindowContext) -> AnyElement>,
-    items: SumTree<ListItem>,
-    logical_scroll_top: Option<ListOffset>,
-    alignment: ListAlignment,
-    overdraw: Pixels,
-    #[allow(clippy::type_complexity)]
-    scroll_handler: Option<Box<dyn FnMut(&ListScrollEvent, &mut WindowContext)>>,
-}
-
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum ListAlignment {
-    Top,
-    Bottom,
-}
-
-pub struct ListScrollEvent {
-    pub visible_range: Range<usize>,
-    pub count: usize,
-}
-
-#[derive(Clone)]
-enum ListItem {
-    Unrendered,
-    Rendered { height: Pixels },
-}
-
-#[derive(Clone, Debug, Default, PartialEq)]
-struct ListItemSummary {
-    count: usize,
-    rendered_count: usize,
-    unrendered_count: usize,
-    height: Pixels,
-}
-
-#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
-struct Count(usize);
-
-#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
-struct RenderedCount(usize);
-
-#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
-struct UnrenderedCount(usize);
-
-#[derive(Clone, Debug, Default)]
-struct Height(Pixels);
-
-impl ListState {
-    pub fn new<F>(
-        element_count: usize,
-        orientation: ListAlignment,
-        overdraw: Pixels,
-        render_item: F,
-    ) -> Self
-    where
-        F: 'static + FnMut(usize, &mut WindowContext) -> AnyElement,
-    {
-        let mut items = SumTree::new();
-        items.extend((0..element_count).map(|_| ListItem::Unrendered), &());
-        Self(Rc::new(RefCell::new(StateInner {
-            last_layout_bounds: None,
-            render_item: Box::new(render_item),
-            items,
-            logical_scroll_top: None,
-            alignment: orientation,
-            overdraw,
-            scroll_handler: None,
-        })))
-    }
-
-    pub fn reset(&self, element_count: usize) {
-        let state = &mut *self.0.borrow_mut();
-        state.logical_scroll_top = None;
-        state.items = SumTree::new();
-        state
-            .items
-            .extend((0..element_count).map(|_| ListItem::Unrendered), &());
-    }
-
-    pub fn item_count(&self) -> usize {
-        self.0.borrow().items.summary().count
-    }
-
-    pub fn splice(&self, old_range: Range<usize>, count: usize) {
-        let state = &mut *self.0.borrow_mut();
-
-        if let Some(ListOffset {
-            item_ix,
-            offset_in_item,
-        }) = state.logical_scroll_top.as_mut()
-        {
-            if old_range.contains(item_ix) {
-                *item_ix = old_range.start;
-                *offset_in_item = px(0.);
-            } else if old_range.end <= *item_ix {
-                *item_ix = *item_ix - (old_range.end - old_range.start) + count;
-            }
-        }
-
-        let mut old_heights = state.items.cursor::<Count>();
-        let mut new_heights = old_heights.slice(&Count(old_range.start), Bias::Right, &());
-        old_heights.seek_forward(&Count(old_range.end), Bias::Right, &());
-
-        new_heights.extend((0..count).map(|_| ListItem::Unrendered), &());
-        new_heights.append(old_heights.suffix(&()), &());
-        drop(old_heights);
-        state.items = new_heights;
-    }
-
-    pub fn set_scroll_handler(
-        &self,
-        handler: impl FnMut(&ListScrollEvent, &mut WindowContext) + 'static,
-    ) {
-        self.0.borrow_mut().scroll_handler = Some(Box::new(handler))
-    }
-
-    pub fn logical_scroll_top(&self) -> ListOffset {
-        self.0.borrow().logical_scroll_top()
-    }
-
-    pub fn scroll_to(&self, mut scroll_top: ListOffset) {
-        let state = &mut *self.0.borrow_mut();
-        let item_count = state.items.summary().count;
-        if scroll_top.item_ix >= item_count {
-            scroll_top.item_ix = item_count;
-            scroll_top.offset_in_item = px(0.);
-        }
-        state.logical_scroll_top = Some(scroll_top);
-    }
-
-    pub fn scroll_to_reveal_item(&self, ix: usize) {
-        let state = &mut *self.0.borrow_mut();
-        let mut scroll_top = state.logical_scroll_top();
-        let height = state
-            .last_layout_bounds
-            .map_or(px(0.), |bounds| bounds.size.height);
-
-        if ix <= scroll_top.item_ix {
-            scroll_top.item_ix = ix;
-            scroll_top.offset_in_item = px(0.);
-        } else {
-            let mut cursor = state.items.cursor::<ListItemSummary>();
-            cursor.seek(&Count(ix + 1), Bias::Right, &());
-            let bottom = cursor.start().height;
-            let goal_top = px(0.).max(bottom - height);
-
-            cursor.seek(&Height(goal_top), Bias::Left, &());
-            let start_ix = cursor.start().count;
-            let start_item_top = cursor.start().height;
-
-            if start_ix >= scroll_top.item_ix {
-                scroll_top.item_ix = start_ix;
-                scroll_top.offset_in_item = goal_top - start_item_top;
-            }
-        }
-
-        state.logical_scroll_top = Some(scroll_top);
-    }
-
-    /// Get the bounds for the given item in window coordinates.
-    pub fn bounds_for_item(&self, ix: usize) -> Option<Bounds<Pixels>> {
-        let state = &*self.0.borrow();
-        let bounds = state.last_layout_bounds.unwrap_or_default();
-        let scroll_top = state.logical_scroll_top();
-
-        if ix < scroll_top.item_ix {
-            return None;
-        }
-
-        let mut cursor = state.items.cursor::<(Count, Height)>();
-        cursor.seek(&Count(scroll_top.item_ix), Bias::Right, &());
-
-        let scroll_top = cursor.start().1 .0 + scroll_top.offset_in_item;
-
-        cursor.seek_forward(&Count(ix), Bias::Right, &());
-        if let Some(&ListItem::Rendered { height }) = cursor.item() {
-            let &(Count(count), Height(top)) = cursor.start();
-            if count == ix {
-                let top = bounds.top() + top - scroll_top;
-                return Some(Bounds::from_corners(
-                    point(bounds.left(), top),
-                    point(bounds.right(), top + height),
-                ));
-            }
-        }
-        None
-    }
-}
-
-impl StateInner {
-    fn visible_range(&self, height: Pixels, scroll_top: &ListOffset) -> Range<usize> {
-        let mut cursor = self.items.cursor::<ListItemSummary>();
-        cursor.seek(&Count(scroll_top.item_ix), Bias::Right, &());
-        let start_y = cursor.start().height + scroll_top.offset_in_item;
-        cursor.seek_forward(&Height(start_y + height), Bias::Left, &());
-        scroll_top.item_ix..cursor.start().count + 1
-    }
-
-    fn scroll(
-        &mut self,
-        scroll_top: &ListOffset,
-        height: Pixels,
-        delta: Point<Pixels>,
-        cx: &mut WindowContext,
-    ) {
-        let scroll_max = (self.items.summary().height - height).max(px(0.));
-        let new_scroll_top = (self.scroll_top(scroll_top) - delta.y)
-            .max(px(0.))
-            .min(scroll_max);
-
-        if self.alignment == ListAlignment::Bottom && new_scroll_top == scroll_max {
-            self.logical_scroll_top = None;
-        } else {
-            let mut cursor = self.items.cursor::<ListItemSummary>();
-            cursor.seek(&Height(new_scroll_top), Bias::Right, &());
-            let item_ix = cursor.start().count;
-            let offset_in_item = new_scroll_top - cursor.start().height;
-            self.logical_scroll_top = Some(ListOffset {
-                item_ix,
-                offset_in_item,
-            });
-        }
-
-        if self.scroll_handler.is_some() {
-            let visible_range = self.visible_range(height, scroll_top);
-            self.scroll_handler.as_mut().unwrap()(
-                &ListScrollEvent {
-                    visible_range,
-                    count: self.items.summary().count,
-                },
-                cx,
-            );
-        }
-
-        cx.notify();
-    }
-
-    fn logical_scroll_top(&self) -> ListOffset {
-        self.logical_scroll_top
-            .unwrap_or_else(|| match self.alignment {
-                ListAlignment::Top => ListOffset {
-                    item_ix: 0,
-                    offset_in_item: px(0.),
-                },
-                ListAlignment::Bottom => ListOffset {
-                    item_ix: self.items.summary().count,
-                    offset_in_item: px(0.),
-                },
-            })
-    }
-
-    fn scroll_top(&self, logical_scroll_top: &ListOffset) -> Pixels {
-        let mut cursor = self.items.cursor::<ListItemSummary>();
-        cursor.seek(&Count(logical_scroll_top.item_ix), Bias::Right, &());
-        cursor.start().height + logical_scroll_top.offset_in_item
-    }
-}
-
-impl std::fmt::Debug for ListItem {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match self {
-            Self::Unrendered => write!(f, "Unrendered"),
-            Self::Rendered { height, .. } => {
-                f.debug_struct("Rendered").field("height", height).finish()
-            }
-        }
-    }
-}
-
-#[derive(Debug, Clone, Copy, Default)]
-pub struct ListOffset {
-    pub item_ix: usize,
-    pub offset_in_item: Pixels,
-}
-
-impl Element for List {
-    type State = ();
-
-    fn request_layout(
-        &mut self,
-        _state: Option<Self::State>,
-        cx: &mut crate::WindowContext,
-    ) -> (crate::LayoutId, Self::State) {
-        let mut style = Style::default();
-        style.refine(&self.style);
-        let layout_id = cx.with_text_style(style.text_style().cloned(), |cx| {
-            cx.request_layout(&style, None)
-        });
-        (layout_id, ())
-    }
-
-    fn paint(
-        &mut self,
-        bounds: crate::Bounds<crate::Pixels>,
-        _state: &mut Self::State,
-        cx: &mut crate::WindowContext,
-    ) {
-        let state = &mut *self.state.0.borrow_mut();
-
-        // If the width of the list has changed, invalidate all cached item heights
-        if state.last_layout_bounds.map_or(true, |last_bounds| {
-            last_bounds.size.width != bounds.size.width
-        }) {
-            state.items = SumTree::from_iter(
-                (0..state.items.summary().count).map(|_| ListItem::Unrendered),
-                &(),
-            )
-        }
-
-        let old_items = state.items.clone();
-        let mut measured_items = VecDeque::new();
-        let mut item_elements = VecDeque::new();
-        let mut rendered_height = px(0.);
-        let mut scroll_top = state.logical_scroll_top();
-
-        let available_item_space = Size {
-            width: AvailableSpace::Definite(bounds.size.width),
-            height: AvailableSpace::MinContent,
-        };
-
-        // Render items after the scroll top, including those in the trailing overdraw
-        let mut cursor = old_items.cursor::<Count>();
-        cursor.seek(&Count(scroll_top.item_ix), Bias::Right, &());
-        for (ix, item) in cursor.by_ref().enumerate() {
-            let visible_height = rendered_height - scroll_top.offset_in_item;
-            if visible_height >= bounds.size.height + state.overdraw {
-                break;
-            }
-
-            // Use the previously cached height if available
-            let mut height = if let ListItem::Rendered { height } = item {
-                Some(*height)
-            } else {
-                None
-            };
-
-            // If we're within the visible area or the height wasn't cached, render and measure the item's element
-            if visible_height < bounds.size.height || height.is_none() {
-                let mut element = (state.render_item)(scroll_top.item_ix + ix, cx);
-                let element_size = element.measure(available_item_space, cx);
-                height = Some(element_size.height);
-                if visible_height < bounds.size.height {
-                    item_elements.push_back(element);
-                }
-            }
-
-            let height = height.unwrap();
-            rendered_height += height;
-            measured_items.push_back(ListItem::Rendered { height });
-        }
-
-        // Prepare to start walking upward from the item at the scroll top.
-        cursor.seek(&Count(scroll_top.item_ix), Bias::Right, &());
-
-        // If the rendered items do not fill the visible region, then adjust
-        // the scroll top upward.
-        if rendered_height - scroll_top.offset_in_item < bounds.size.height {
-            while rendered_height < bounds.size.height {
-                cursor.prev(&());
-                if cursor.item().is_some() {
-                    let mut element = (state.render_item)(cursor.start().0, cx);
-                    let element_size = element.measure(available_item_space, cx);
-
-                    rendered_height += element_size.height;
-                    measured_items.push_front(ListItem::Rendered {
-                        height: element_size.height,
-                    });
-                    item_elements.push_front(element)
-                } else {
-                    break;
-                }
-            }
-
-            scroll_top = ListOffset {
-                item_ix: cursor.start().0,
-                offset_in_item: rendered_height - bounds.size.height,
-            };
-
-            match state.alignment {
-                ListAlignment::Top => {
-                    scroll_top.offset_in_item = scroll_top.offset_in_item.max(px(0.));
-                    state.logical_scroll_top = Some(scroll_top);
-                }
-                ListAlignment::Bottom => {
-                    scroll_top = ListOffset {
-                        item_ix: cursor.start().0,
-                        offset_in_item: rendered_height - bounds.size.height,
-                    };
-                    state.logical_scroll_top = None;
-                }
-            };
-        }
-
-        // Measure items in the leading overdraw
-        let mut leading_overdraw = scroll_top.offset_in_item;
-        while leading_overdraw < state.overdraw {
-            cursor.prev(&());
-            if let Some(item) = cursor.item() {
-                let height = if let ListItem::Rendered { height } = item {
-                    *height
-                } else {
-                    let mut element = (state.render_item)(cursor.start().0, cx);
-                    element.measure(available_item_space, cx).height
-                };
-
-                leading_overdraw += height;
-                measured_items.push_front(ListItem::Rendered { height });
-            } else {
-                break;
-            }
-        }
-
-        let measured_range = cursor.start().0..(cursor.start().0 + measured_items.len());
-        let mut cursor = old_items.cursor::<Count>();
-        let mut new_items = cursor.slice(&Count(measured_range.start), Bias::Right, &());
-        new_items.extend(measured_items, &());
-        cursor.seek(&Count(measured_range.end), Bias::Right, &());
-        new_items.append(cursor.suffix(&()), &());
-
-        // Paint the visible items
-        let mut item_origin = bounds.origin;
-        item_origin.y -= scroll_top.offset_in_item;
-        for item_element in &mut item_elements {
-            let item_height = item_element.measure(available_item_space, cx).height;
-            item_element.draw(item_origin, available_item_space, cx);
-            item_origin.y += item_height;
-        }
-
-        state.items = new_items;
-        state.last_layout_bounds = Some(bounds);
-
-        let list_state = self.state.clone();
-        let height = bounds.size.height;
-        cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
-            if phase == DispatchPhase::Bubble
-                && bounds.contains(&event.position)
-                && cx.was_top_layer(&event.position, cx.stacking_order())
-            {
-                list_state.0.borrow_mut().scroll(
-                    &scroll_top,
-                    height,
-                    event.delta.pixel_delta(px(20.)),
-                    cx,
-                )
-            }
-        });
-    }
-}
-
-impl IntoElement for List {
-    type Element = Self;
-
-    fn element_id(&self) -> Option<crate::ElementId> {
-        None
-    }
-
-    fn into_element(self) -> Self::Element {
-        self
-    }
-}
-
-impl Styled for List {
-    fn style(&mut self) -> &mut StyleRefinement {
-        &mut self.style
-    }
-}
-
-impl sum_tree::Item for ListItem {
-    type Summary = ListItemSummary;
-
-    fn summary(&self) -> Self::Summary {
-        match self {
-            ListItem::Unrendered => ListItemSummary {
-                count: 1,
-                rendered_count: 0,
-                unrendered_count: 1,
-                height: px(0.),
-            },
-            ListItem::Rendered { height } => ListItemSummary {
-                count: 1,
-                rendered_count: 1,
-                unrendered_count: 0,
-                height: *height,
-            },
-        }
-    }
-}
-
-impl sum_tree::Summary for ListItemSummary {
-    type Context = ();
-
-    fn add_summary(&mut self, summary: &Self, _: &()) {
-        self.count += summary.count;
-        self.rendered_count += summary.rendered_count;
-        self.unrendered_count += summary.unrendered_count;
-        self.height += summary.height;
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, ListItemSummary> for Count {
-    fn add_summary(&mut self, summary: &'a ListItemSummary, _: &()) {
-        self.0 += summary.count;
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, ListItemSummary> for RenderedCount {
-    fn add_summary(&mut self, summary: &'a ListItemSummary, _: &()) {
-        self.0 += summary.rendered_count;
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, ListItemSummary> for UnrenderedCount {
-    fn add_summary(&mut self, summary: &'a ListItemSummary, _: &()) {
-        self.0 += summary.unrendered_count;
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, ListItemSummary> for Height {
-    fn add_summary(&mut self, summary: &'a ListItemSummary, _: &()) {
-        self.0 += summary.height;
-    }
-}
-
-impl<'a> sum_tree::SeekTarget<'a, ListItemSummary, ListItemSummary> for Count {
-    fn cmp(&self, other: &ListItemSummary, _: &()) -> std::cmp::Ordering {
-        self.0.partial_cmp(&other.count).unwrap()
-    }
-}
-
-impl<'a> sum_tree::SeekTarget<'a, ListItemSummary, ListItemSummary> for Height {
-    fn cmp(&self, other: &ListItemSummary, _: &()) -> std::cmp::Ordering {
-        self.0.partial_cmp(&other.height).unwrap()
-    }
-}

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

@@ -1,241 +0,0 @@
-use smallvec::SmallVec;
-use taffy::style::{Display, Position};
-
-use crate::{
-    point, AnyElement, BorrowWindow, Bounds, Element, IntoElement, LayoutId, ParentElement, Pixels,
-    Point, Size, Style, WindowContext,
-};
-
-pub struct OverlayState {
-    child_layout_ids: SmallVec<[LayoutId; 4]>,
-}
-
-pub struct Overlay {
-    children: SmallVec<[AnyElement; 2]>,
-    anchor_corner: AnchorCorner,
-    fit_mode: OverlayFitMode,
-    // todo!();
-    anchor_position: Option<Point<Pixels>>,
-    // position_mode: OverlayPositionMode,
-}
-
-/// overlay gives you a floating element that will avoid overflowing the window bounds.
-/// Its children should have no margin to avoid measurement issues.
-pub fn overlay() -> Overlay {
-    Overlay {
-        children: SmallVec::new(),
-        anchor_corner: AnchorCorner::TopLeft,
-        fit_mode: OverlayFitMode::SwitchAnchor,
-        anchor_position: None,
-    }
-}
-
-impl Overlay {
-    /// Sets which corner of the overlay should be anchored to the current position.
-    pub fn anchor(mut self, anchor: AnchorCorner) -> Self {
-        self.anchor_corner = anchor;
-        self
-    }
-
-    /// Sets the position in window co-ordinates
-    /// (otherwise the location the overlay is rendered is used)
-    pub fn position(mut self, anchor: Point<Pixels>) -> Self {
-        self.anchor_position = Some(anchor);
-        self
-    }
-
-    /// Snap to window edge instead of switching anchor corner when an overflow would occur.
-    pub fn snap_to_window(mut self) -> Self {
-        self.fit_mode = OverlayFitMode::SnapToWindow;
-        self
-    }
-}
-
-impl ParentElement for Overlay {
-    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
-        &mut self.children
-    }
-}
-
-impl Element for Overlay {
-    type State = OverlayState;
-
-    fn request_layout(
-        &mut self,
-        _: Option<Self::State>,
-        cx: &mut WindowContext,
-    ) -> (crate::LayoutId, Self::State) {
-        let child_layout_ids = self
-            .children
-            .iter_mut()
-            .map(|child| child.request_layout(cx))
-            .collect::<SmallVec<_>>();
-
-        let overlay_style = Style {
-            position: Position::Absolute,
-            display: Display::Flex,
-            ..Style::default()
-        };
-
-        let layout_id = cx.request_layout(&overlay_style, child_layout_ids.iter().copied());
-
-        (layout_id, OverlayState { child_layout_ids })
-    }
-
-    fn paint(
-        &mut self,
-        bounds: crate::Bounds<crate::Pixels>,
-        element_state: &mut Self::State,
-        cx: &mut WindowContext,
-    ) {
-        if element_state.child_layout_ids.is_empty() {
-            return;
-        }
-
-        let mut child_min = point(Pixels::MAX, Pixels::MAX);
-        let mut child_max = Point::default();
-        for child_layout_id in &element_state.child_layout_ids {
-            let child_bounds = cx.layout_bounds(*child_layout_id);
-            child_min = child_min.min(&child_bounds.origin);
-            child_max = child_max.max(&child_bounds.lower_right());
-        }
-        let size: Size<Pixels> = (child_max - child_min).into();
-        let origin = self.anchor_position.unwrap_or(bounds.origin);
-
-        let mut desired = self.anchor_corner.get_bounds(origin, size);
-        let limits = Bounds {
-            origin: Point::default(),
-            size: cx.viewport_size(),
-        };
-
-        if self.fit_mode == OverlayFitMode::SwitchAnchor {
-            let mut anchor_corner = self.anchor_corner;
-
-            if desired.left() < limits.left() || desired.right() > limits.right() {
-                let switched = anchor_corner
-                    .switch_axis(Axis::Horizontal)
-                    .get_bounds(origin, size);
-                if !(switched.left() < limits.left() || switched.right() > limits.right()) {
-                    anchor_corner = anchor_corner.switch_axis(Axis::Horizontal);
-                    desired = switched
-                }
-            }
-
-            if desired.top() < limits.top() || desired.bottom() > limits.bottom() {
-                let switched = anchor_corner
-                    .switch_axis(Axis::Vertical)
-                    .get_bounds(origin, size);
-                if !(switched.top() < limits.top() || switched.bottom() > limits.bottom()) {
-                    desired = switched;
-                }
-            }
-        }
-
-        // Snap the horizontal edges of the overlay to the horizontal edges of the window if
-        // its horizontal bounds overflow, aligning to the left if it is wider than the limits.
-        if desired.right() > limits.right() {
-            desired.origin.x -= desired.right() - limits.right();
-        }
-        if desired.left() < limits.left() {
-            desired.origin.x = limits.origin.x;
-        }
-
-        // Snap the vertical edges of the overlay to the vertical edges of the window if
-        // its vertical bounds overflow, aligning to the top if it is taller than the limits.
-        if desired.bottom() > limits.bottom() {
-            desired.origin.y -= desired.bottom() - limits.bottom();
-        }
-        if desired.top() < limits.top() {
-            desired.origin.y = limits.origin.y;
-        }
-
-        let mut offset = cx.element_offset() + desired.origin - bounds.origin;
-        offset = point(offset.x.round(), offset.y.round());
-        cx.with_absolute_element_offset(offset, |cx| {
-            cx.break_content_mask(|cx| {
-                for child in &mut self.children {
-                    child.paint(cx);
-                }
-            })
-        })
-    }
-}
-
-impl IntoElement for Overlay {
-    type Element = Self;
-
-    fn element_id(&self) -> Option<crate::ElementId> {
-        None
-    }
-
-    fn into_element(self) -> Self::Element {
-        self
-    }
-}
-
-enum Axis {
-    Horizontal,
-    Vertical,
-}
-
-#[derive(Copy, Clone, PartialEq)]
-pub enum OverlayFitMode {
-    SnapToWindow,
-    SwitchAnchor,
-}
-
-#[derive(Clone, Copy, PartialEq, Eq)]
-pub enum AnchorCorner {
-    TopLeft,
-    TopRight,
-    BottomLeft,
-    BottomRight,
-}
-
-impl AnchorCorner {
-    fn get_bounds(&self, origin: Point<Pixels>, size: Size<Pixels>) -> Bounds<Pixels> {
-        let origin = match self {
-            Self::TopLeft => origin,
-            Self::TopRight => Point {
-                x: origin.x - size.width,
-                y: origin.y,
-            },
-            Self::BottomLeft => Point {
-                x: origin.x,
-                y: origin.y - size.height,
-            },
-            Self::BottomRight => Point {
-                x: origin.x - size.width,
-                y: origin.y - size.height,
-            },
-        };
-
-        Bounds { origin, size }
-    }
-
-    pub fn corner(&self, bounds: Bounds<Pixels>) -> Point<Pixels> {
-        match self {
-            Self::TopLeft => bounds.origin,
-            Self::TopRight => bounds.upper_right(),
-            Self::BottomLeft => bounds.lower_left(),
-            Self::BottomRight => bounds.lower_right(),
-        }
-    }
-
-    fn switch_axis(self, axis: Axis) -> Self {
-        match axis {
-            Axis::Vertical => match self {
-                AnchorCorner::TopLeft => AnchorCorner::BottomLeft,
-                AnchorCorner::TopRight => AnchorCorner::BottomRight,
-                AnchorCorner::BottomLeft => AnchorCorner::TopLeft,
-                AnchorCorner::BottomRight => AnchorCorner::TopRight,
-            },
-            Axis::Horizontal => match self {
-                AnchorCorner::TopLeft => AnchorCorner::TopRight,
-                AnchorCorner::TopRight => AnchorCorner::TopLeft,
-                AnchorCorner::BottomLeft => AnchorCorner::BottomRight,
-                AnchorCorner::BottomRight => AnchorCorner::BottomLeft,
-            },
-        }
-    }
-}

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

@@ -1,78 +0,0 @@
-use crate::{
-    Bounds, Element, ElementId, InteractiveElement, InteractiveElementState, Interactivity,
-    IntoElement, LayoutId, Pixels, SharedString, StyleRefinement, Styled, WindowContext,
-};
-use util::ResultExt;
-
-pub struct Svg {
-    interactivity: Interactivity,
-    path: Option<SharedString>,
-}
-
-pub fn svg() -> Svg {
-    Svg {
-        interactivity: Interactivity::default(),
-        path: None,
-    }
-}
-
-impl Svg {
-    pub fn path(mut self, path: impl Into<SharedString>) -> Self {
-        self.path = Some(path.into());
-        self
-    }
-}
-
-impl Element for Svg {
-    type State = InteractiveElementState;
-
-    fn request_layout(
-        &mut self,
-        element_state: Option<Self::State>,
-        cx: &mut WindowContext,
-    ) -> (LayoutId, Self::State) {
-        self.interactivity.layout(element_state, cx, |style, cx| {
-            cx.request_layout(&style, None)
-        })
-    }
-
-    fn paint(
-        &mut self,
-        bounds: Bounds<Pixels>,
-        element_state: &mut Self::State,
-        cx: &mut WindowContext,
-    ) where
-        Self: Sized,
-    {
-        self.interactivity
-            .paint(bounds, bounds.size, element_state, cx, |style, _, cx| {
-                if let Some((path, color)) = self.path.as_ref().zip(style.text.color) {
-                    cx.paint_svg(bounds, path.clone(), color).log_err();
-                }
-            })
-    }
-}
-
-impl IntoElement for Svg {
-    type Element = Self;
-
-    fn element_id(&self) -> Option<ElementId> {
-        self.interactivity.element_id.clone()
-    }
-
-    fn into_element(self) -> Self::Element {
-        self
-    }
-}
-
-impl Styled for Svg {
-    fn style(&mut self) -> &mut StyleRefinement {
-        &mut self.interactivity.base_style
-    }
-}
-
-impl InteractiveElement for Svg {
-    fn interactivity(&mut self) -> &mut Interactivity {
-        &mut self.interactivity
-    }
-}

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

@@ -1,423 +0,0 @@
-use crate::{
-    Bounds, DispatchPhase, Element, ElementId, HighlightStyle, IntoElement, LayoutId,
-    MouseDownEvent, MouseUpEvent, Pixels, Point, SharedString, Size, TextRun, TextStyle,
-    WhiteSpace, WindowContext, WrappedLine,
-};
-use anyhow::anyhow;
-use parking_lot::{Mutex, MutexGuard};
-use smallvec::SmallVec;
-use std::{cell::Cell, mem, ops::Range, rc::Rc, sync::Arc};
-use util::ResultExt;
-
-impl Element for &'static str {
-    type State = TextState;
-
-    fn request_layout(
-        &mut self,
-        _: Option<Self::State>,
-        cx: &mut WindowContext,
-    ) -> (LayoutId, Self::State) {
-        let mut state = TextState::default();
-        let layout_id = state.layout(SharedString::from(*self), None, cx);
-        (layout_id, state)
-    }
-
-    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut WindowContext) {
-        state.paint(bounds, self, cx)
-    }
-}
-
-impl IntoElement for &'static str {
-    type Element = Self;
-
-    fn element_id(&self) -> Option<ElementId> {
-        None
-    }
-
-    fn into_element(self) -> Self::Element {
-        self
-    }
-}
-
-impl Element for SharedString {
-    type State = TextState;
-
-    fn request_layout(
-        &mut self,
-        _: Option<Self::State>,
-        cx: &mut WindowContext,
-    ) -> (LayoutId, Self::State) {
-        let mut state = TextState::default();
-        let layout_id = state.layout(self.clone(), None, cx);
-        (layout_id, state)
-    }
-
-    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut TextState, cx: &mut WindowContext) {
-        let text_str: &str = self.as_ref();
-        state.paint(bounds, text_str, cx)
-    }
-}
-
-impl IntoElement for SharedString {
-    type Element = Self;
-
-    fn element_id(&self) -> Option<ElementId> {
-        None
-    }
-
-    fn into_element(self) -> Self::Element {
-        self
-    }
-}
-
-/// Renders text with runs of different styles.
-///
-/// Callers are responsible for setting the correct style for each run.
-/// For text with a uniform style, you can usually avoid calling this constructor
-/// and just pass text directly.
-pub struct StyledText {
-    text: SharedString,
-    runs: Option<Vec<TextRun>>,
-}
-
-impl StyledText {
-    pub fn new(text: impl Into<SharedString>) -> Self {
-        StyledText {
-            text: text.into(),
-            runs: None,
-        }
-    }
-
-    pub fn with_highlights(
-        mut self,
-        default_style: &TextStyle,
-        highlights: impl IntoIterator<Item = (Range<usize>, HighlightStyle)>,
-    ) -> Self {
-        let mut runs = Vec::new();
-        let mut ix = 0;
-        for (range, highlight) in highlights {
-            if ix < range.start {
-                runs.push(default_style.clone().to_run(range.start - ix));
-            }
-            runs.push(
-                default_style
-                    .clone()
-                    .highlight(highlight)
-                    .to_run(range.len()),
-            );
-            ix = range.end;
-        }
-        if ix < self.text.len() {
-            runs.push(default_style.to_run(self.text.len() - ix));
-        }
-        self.runs = Some(runs);
-        self
-    }
-}
-
-impl Element for StyledText {
-    type State = TextState;
-
-    fn request_layout(
-        &mut self,
-        _: Option<Self::State>,
-        cx: &mut WindowContext,
-    ) -> (LayoutId, Self::State) {
-        let mut state = TextState::default();
-        let layout_id = state.layout(self.text.clone(), self.runs.take(), cx);
-        (layout_id, state)
-    }
-
-    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
-        state.paint(bounds, &self.text, cx)
-    }
-}
-
-impl IntoElement for StyledText {
-    type Element = Self;
-
-    fn element_id(&self) -> Option<crate::ElementId> {
-        None
-    }
-
-    fn into_element(self) -> Self::Element {
-        self
-    }
-}
-
-#[derive(Default, Clone)]
-pub struct TextState(Arc<Mutex<Option<TextStateInner>>>);
-
-struct TextStateInner {
-    lines: SmallVec<[WrappedLine; 1]>,
-    line_height: Pixels,
-    wrap_width: Option<Pixels>,
-    size: Option<Size<Pixels>>,
-}
-
-impl TextState {
-    fn lock(&self) -> MutexGuard<Option<TextStateInner>> {
-        self.0.lock()
-    }
-
-    fn layout(
-        &mut self,
-        text: SharedString,
-        runs: Option<Vec<TextRun>>,
-        cx: &mut WindowContext,
-    ) -> LayoutId {
-        let text_style = cx.text_style();
-        let font_size = text_style.font_size.to_pixels(cx.rem_size());
-        let line_height = text_style
-            .line_height
-            .to_pixels(font_size.into(), cx.rem_size());
-
-        let runs = if let Some(runs) = runs {
-            runs
-        } else {
-            vec![text_style.to_run(text.len())]
-        };
-
-        let layout_id = cx.request_measured_layout(Default::default(), {
-            let element_state = self.clone();
-
-            move |known_dimensions, available_space, cx| {
-                let wrap_width = if text_style.white_space == WhiteSpace::Normal {
-                    known_dimensions.width.or(match available_space.width {
-                        crate::AvailableSpace::Definite(x) => Some(x),
-                        _ => None,
-                    })
-                } else {
-                    None
-                };
-
-                if let Some(text_state) = element_state.0.lock().as_ref() {
-                    if text_state.size.is_some()
-                        && (wrap_width.is_none() || wrap_width == text_state.wrap_width)
-                    {
-                        return text_state.size.unwrap();
-                    }
-                }
-
-                let Some(lines) = cx
-                    .text_system()
-                    .shape_text(
-                        &text, font_size, &runs, wrap_width, // Wrap if we know the width.
-                    )
-                    .log_err()
-                else {
-                    element_state.lock().replace(TextStateInner {
-                        lines: Default::default(),
-                        line_height,
-                        wrap_width,
-                        size: Some(Size::default()),
-                    });
-                    return Size::default();
-                };
-
-                let mut size: Size<Pixels> = Size::default();
-                for line in &lines {
-                    let line_size = line.size(line_height);
-                    size.height += line_size.height;
-                    size.width = size.width.max(line_size.width).ceil();
-                }
-
-                element_state.lock().replace(TextStateInner {
-                    lines,
-                    line_height,
-                    wrap_width,
-                    size: Some(size),
-                });
-
-                size
-            }
-        });
-
-        layout_id
-    }
-
-    fn paint(&mut self, bounds: Bounds<Pixels>, text: &str, cx: &mut WindowContext) {
-        let element_state = self.lock();
-        let element_state = element_state
-            .as_ref()
-            .ok_or_else(|| anyhow!("measurement has not been performed on {}", text))
-            .unwrap();
-
-        let line_height = element_state.line_height;
-        let mut line_origin = bounds.origin;
-        for line in &element_state.lines {
-            line.paint(line_origin, line_height, cx).log_err();
-            line_origin.y += line.size(line_height).height;
-        }
-    }
-
-    fn index_for_position(&self, bounds: Bounds<Pixels>, position: Point<Pixels>) -> Option<usize> {
-        if !bounds.contains(&position) {
-            return None;
-        }
-
-        let element_state = self.lock();
-        let element_state = element_state
-            .as_ref()
-            .expect("measurement has not been performed");
-
-        let line_height = element_state.line_height;
-        let mut line_origin = bounds.origin;
-        let mut line_start_ix = 0;
-        for line in &element_state.lines {
-            let line_bottom = line_origin.y + line.size(line_height).height;
-            if position.y > line_bottom {
-                line_origin.y = line_bottom;
-                line_start_ix += line.len() + 1;
-            } else {
-                let position_within_line = position - line_origin;
-                let index_within_line =
-                    line.index_for_position(position_within_line, line_height)?;
-                return Some(line_start_ix + index_within_line);
-            }
-        }
-
-        None
-    }
-}
-
-pub struct InteractiveText {
-    element_id: ElementId,
-    text: StyledText,
-    click_listener:
-        Option<Box<dyn Fn(&[Range<usize>], InteractiveTextClickEvent, &mut WindowContext<'_>)>>,
-    clickable_ranges: Vec<Range<usize>>,
-}
-
-struct InteractiveTextClickEvent {
-    mouse_down_index: usize,
-    mouse_up_index: usize,
-}
-
-pub struct InteractiveTextState {
-    text_state: TextState,
-    mouse_down_index: Rc<Cell<Option<usize>>>,
-}
-
-impl InteractiveText {
-    pub fn new(id: impl Into<ElementId>, text: StyledText) -> Self {
-        Self {
-            element_id: id.into(),
-            text,
-            click_listener: None,
-            clickable_ranges: Vec::new(),
-        }
-    }
-
-    pub fn on_click(
-        mut self,
-        ranges: Vec<Range<usize>>,
-        listener: impl Fn(usize, &mut WindowContext<'_>) + 'static,
-    ) -> Self {
-        self.click_listener = Some(Box::new(move |ranges, event, cx| {
-            for (range_ix, range) in ranges.iter().enumerate() {
-                if range.contains(&event.mouse_down_index) && range.contains(&event.mouse_up_index)
-                {
-                    listener(range_ix, cx);
-                }
-            }
-        }));
-        self.clickable_ranges = ranges;
-        self
-    }
-}
-
-impl Element for InteractiveText {
-    type State = InteractiveTextState;
-
-    fn request_layout(
-        &mut self,
-        state: Option<Self::State>,
-        cx: &mut WindowContext,
-    ) -> (LayoutId, Self::State) {
-        if let Some(InteractiveTextState {
-            mouse_down_index, ..
-        }) = state
-        {
-            let (layout_id, text_state) = self.text.request_layout(None, cx);
-            let element_state = InteractiveTextState {
-                text_state,
-                mouse_down_index,
-            };
-            (layout_id, element_state)
-        } else {
-            let (layout_id, text_state) = self.text.request_layout(None, cx);
-            let element_state = InteractiveTextState {
-                text_state,
-                mouse_down_index: Rc::default(),
-            };
-            (layout_id, element_state)
-        }
-    }
-
-    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
-        if let Some(click_listener) = self.click_listener.take() {
-            let mouse_position = cx.mouse_position();
-            if let Some(ix) = state.text_state.index_for_position(bounds, mouse_position) {
-                if self
-                    .clickable_ranges
-                    .iter()
-                    .any(|range| range.contains(&ix))
-                    && cx.was_top_layer(&mouse_position, cx.stacking_order())
-                {
-                    cx.set_cursor_style(crate::CursorStyle::PointingHand)
-                }
-            }
-
-            let text_state = state.text_state.clone();
-            let mouse_down = state.mouse_down_index.clone();
-            if let Some(mouse_down_index) = mouse_down.get() {
-                let clickable_ranges = mem::take(&mut self.clickable_ranges);
-                cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| {
-                    if phase == DispatchPhase::Bubble {
-                        if let Some(mouse_up_index) =
-                            text_state.index_for_position(bounds, event.position)
-                        {
-                            click_listener(
-                                &clickable_ranges,
-                                InteractiveTextClickEvent {
-                                    mouse_down_index,
-                                    mouse_up_index,
-                                },
-                                cx,
-                            )
-                        }
-
-                        mouse_down.take();
-                        cx.notify();
-                    }
-                });
-            } else {
-                cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
-                    if phase == DispatchPhase::Bubble {
-                        if let Some(mouse_down_index) =
-                            text_state.index_for_position(bounds, event.position)
-                        {
-                            mouse_down.set(Some(mouse_down_index));
-                            cx.notify();
-                        }
-                    }
-                });
-            }
-        }
-
-        self.text.paint(bounds, &mut state.text_state, cx)
-    }
-}
-
-impl IntoElement for InteractiveText {
-    type Element = Self;
-
-    fn element_id(&self) -> Option<ElementId> {
-        Some(self.element_id.clone())
-    }
-
-    fn into_element(self) -> Self::Element {
-        self
-    }
-}

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

@@ -1,316 +0,0 @@
-use crate::{
-    point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Element,
-    ElementId, InteractiveElement, InteractiveElementState, Interactivity, IntoElement, LayoutId,
-    Pixels, Point, Render, Size, StyleRefinement, Styled, View, ViewContext, WindowContext,
-};
-use smallvec::SmallVec;
-use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
-use taffy::style::Overflow;
-
-/// uniform_list provides lazy rendering for a set of items that are of uniform height.
-/// When rendered into a container with overflow-y: hidden and a fixed (or max) height,
-/// uniform_list will only render the visible subset of items.
-#[track_caller]
-pub fn uniform_list<I, R, V>(
-    view: View<V>,
-    id: I,
-    item_count: usize,
-    f: impl 'static + Fn(&mut V, Range<usize>, &mut ViewContext<V>) -> Vec<R>,
-) -> UniformList
-where
-    I: Into<ElementId>,
-    R: IntoElement,
-    V: Render,
-{
-    let id = id.into();
-    let mut base_style = StyleRefinement::default();
-    base_style.overflow.y = Some(Overflow::Scroll);
-
-    let render_range = move |range, cx: &mut WindowContext| {
-        view.update(cx, |this, cx| {
-            f(this, range, cx)
-                .into_iter()
-                .map(|component| component.into_any_element())
-                .collect()
-        })
-    };
-
-    UniformList {
-        id: id.clone(),
-        item_count,
-        item_to_measure_index: 0,
-        render_items: Box::new(render_range),
-        interactivity: Interactivity {
-            element_id: Some(id),
-            base_style: Box::new(base_style),
-
-            #[cfg(debug_assertions)]
-            location: Some(*core::panic::Location::caller()),
-
-            ..Default::default()
-        },
-        scroll_handle: None,
-    }
-}
-
-pub struct UniformList {
-    id: ElementId,
-    item_count: usize,
-    item_to_measure_index: usize,
-    render_items:
-        Box<dyn for<'a> Fn(Range<usize>, &'a mut WindowContext) -> SmallVec<[AnyElement; 64]>>,
-    interactivity: Interactivity,
-    scroll_handle: Option<UniformListScrollHandle>,
-}
-
-#[derive(Clone, Default)]
-pub struct UniformListScrollHandle(Rc<RefCell<Option<ScrollHandleState>>>);
-
-#[derive(Clone, Debug)]
-struct ScrollHandleState {
-    item_height: Pixels,
-    list_height: Pixels,
-    scroll_offset: Rc<RefCell<Point<Pixels>>>,
-}
-
-impl UniformListScrollHandle {
-    pub fn new() -> Self {
-        Self(Rc::new(RefCell::new(None)))
-    }
-
-    pub fn scroll_to_item(&self, ix: usize) {
-        if let Some(state) = &*self.0.borrow() {
-            let mut scroll_offset = state.scroll_offset.borrow_mut();
-            let item_top = state.item_height * ix;
-            let item_bottom = item_top + state.item_height;
-            let scroll_top = -scroll_offset.y;
-            if item_top < scroll_top {
-                scroll_offset.y = -item_top;
-            } else if item_bottom > scroll_top + state.list_height {
-                scroll_offset.y = -(item_bottom - state.list_height);
-            }
-        }
-    }
-
-    pub fn scroll_top(&self) -> Pixels {
-        if let Some(state) = &*self.0.borrow() {
-            -state.scroll_offset.borrow().y
-        } else {
-            Pixels::ZERO
-        }
-    }
-}
-
-impl Styled for UniformList {
-    fn style(&mut self) -> &mut StyleRefinement {
-        &mut self.interactivity.base_style
-    }
-}
-
-#[derive(Default)]
-pub struct UniformListState {
-    interactive: InteractiveElementState,
-    item_size: Size<Pixels>,
-}
-
-impl Element for UniformList {
-    type State = UniformListState;
-
-    fn request_layout(
-        &mut self,
-        state: Option<Self::State>,
-        cx: &mut WindowContext,
-    ) -> (LayoutId, Self::State) {
-        let max_items = self.item_count;
-        let item_size = state
-            .as_ref()
-            .map(|s| s.item_size)
-            .unwrap_or_else(|| self.measure_item(None, cx));
-
-        let (layout_id, interactive) =
-            self.interactivity
-                .layout(state.map(|s| s.interactive), cx, |style, cx| {
-                    cx.request_measured_layout(
-                        style,
-                        move |known_dimensions, available_space, _cx| {
-                            let desired_height = item_size.height * max_items;
-                            let width =
-                                known_dimensions
-                                    .width
-                                    .unwrap_or(match available_space.width {
-                                        AvailableSpace::Definite(x) => x,
-                                        AvailableSpace::MinContent | AvailableSpace::MaxContent => {
-                                            item_size.width
-                                        }
-                                    });
-
-                            let height = match available_space.height {
-                                AvailableSpace::Definite(height) => desired_height.min(height),
-                                AvailableSpace::MinContent | AvailableSpace::MaxContent => {
-                                    desired_height
-                                }
-                            };
-                            size(width, height)
-                        },
-                    )
-                });
-
-        let element_state = UniformListState {
-            interactive,
-            item_size,
-        };
-
-        (layout_id, element_state)
-    }
-
-    fn paint(
-        &mut self,
-        bounds: Bounds<crate::Pixels>,
-        element_state: &mut Self::State,
-        cx: &mut WindowContext,
-    ) {
-        let style =
-            self.interactivity
-                .compute_style(Some(bounds), &mut element_state.interactive, cx);
-        let border = style.border_widths.to_pixels(cx.rem_size());
-        let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
-
-        let padded_bounds = Bounds::from_corners(
-            bounds.origin + point(border.left + padding.left, border.top + padding.top),
-            bounds.lower_right()
-                - point(border.right + padding.right, border.bottom + padding.bottom),
-        );
-
-        let item_size = element_state.item_size;
-        let content_size = Size {
-            width: padded_bounds.size.width,
-            height: item_size.height * self.item_count + padding.top + padding.bottom,
-        };
-
-        let shared_scroll_offset = element_state
-            .interactive
-            .scroll_offset
-            .get_or_insert_with(|| {
-                if let Some(scroll_handle) = self.scroll_handle.as_ref() {
-                    if let Some(scroll_handle) = scroll_handle.0.borrow().as_ref() {
-                        return scroll_handle.scroll_offset.clone();
-                    }
-                }
-
-                Rc::default()
-            })
-            .clone();
-
-        let item_height = self.measure_item(Some(padded_bounds.size.width), cx).height;
-
-        self.interactivity.paint(
-            bounds,
-            content_size,
-            &mut element_state.interactive,
-            cx,
-            |style, mut scroll_offset, cx| {
-                let border = style.border_widths.to_pixels(cx.rem_size());
-                let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
-
-                let padded_bounds = Bounds::from_corners(
-                    bounds.origin + point(border.left + padding.left, border.top),
-                    bounds.lower_right() - point(border.right + padding.right, border.bottom),
-                );
-
-                if self.item_count > 0 {
-                    let content_height =
-                        item_height * self.item_count + padding.top + padding.bottom;
-                    let min_scroll_offset = padded_bounds.size.height - content_height;
-                    let is_scrolled = scroll_offset.y != px(0.);
-
-                    if is_scrolled && scroll_offset.y < min_scroll_offset {
-                        shared_scroll_offset.borrow_mut().y = min_scroll_offset;
-                        scroll_offset.y = min_scroll_offset;
-                    }
-
-                    if let Some(scroll_handle) = self.scroll_handle.clone() {
-                        scroll_handle.0.borrow_mut().replace(ScrollHandleState {
-                            item_height,
-                            list_height: padded_bounds.size.height,
-                            scroll_offset: shared_scroll_offset,
-                        });
-                    }
-
-                    let first_visible_element_ix =
-                        (-(scroll_offset.y + padding.top) / item_height).floor() as usize;
-                    let last_visible_element_ix = ((-scroll_offset.y + padded_bounds.size.height)
-                        / item_height)
-                        .ceil() as usize;
-                    let visible_range = first_visible_element_ix
-                        ..cmp::min(last_visible_element_ix, self.item_count);
-
-                    let mut items = (self.render_items)(visible_range.clone(), cx);
-                    cx.with_z_index(1, |cx| {
-                        let content_mask = ContentMask { bounds };
-                        cx.with_content_mask(Some(content_mask), |cx| {
-                            for (item, ix) in items.iter_mut().zip(visible_range) {
-                                let item_origin = padded_bounds.origin
-                                    + point(
-                                        px(0.),
-                                        item_height * ix + scroll_offset.y + padding.top,
-                                    );
-                                let available_space = size(
-                                    AvailableSpace::Definite(padded_bounds.size.width),
-                                    AvailableSpace::Definite(item_height),
-                                );
-                                item.draw(item_origin, available_space, cx);
-                            }
-                        });
-                    });
-                }
-            },
-        )
-    }
-}
-
-impl IntoElement for UniformList {
-    type Element = Self;
-
-    fn element_id(&self) -> Option<crate::ElementId> {
-        Some(self.id.clone())
-    }
-
-    fn into_element(self) -> Self::Element {
-        self
-    }
-}
-
-impl UniformList {
-    pub fn with_width_from_item(mut self, item_index: Option<usize>) -> Self {
-        self.item_to_measure_index = item_index.unwrap_or(0);
-        self
-    }
-
-    fn measure_item(&self, list_width: Option<Pixels>, cx: &mut WindowContext) -> Size<Pixels> {
-        if self.item_count == 0 {
-            return Size::default();
-        }
-
-        let item_ix = cmp::min(self.item_to_measure_index, self.item_count - 1);
-        let mut items = (self.render_items)(item_ix..item_ix + 1, cx);
-        let mut item_to_measure = items.pop().unwrap();
-        let available_space = size(
-            list_width.map_or(AvailableSpace::MinContent, |width| {
-                AvailableSpace::Definite(width)
-            }),
-            AvailableSpace::MinContent,
-        );
-        item_to_measure.measure(available_space, cx)
-    }
-
-    pub fn track_scroll(mut self, handle: UniformListScrollHandle) -> Self {
-        self.scroll_handle = Some(handle);
-        self
-    }
-}
-
-impl InteractiveElement for UniformList {
-    fn interactivity(&mut self) -> &mut crate::Interactivity {
-        &mut self.interactivity
-    }
-}

crates/gpui2/src/executor.rs 🔗

@@ -1,402 +0,0 @@
-use crate::{AppContext, PlatformDispatcher};
-use futures::{channel::mpsc, pin_mut, FutureExt};
-use smol::prelude::*;
-use std::{
-    fmt::Debug,
-    marker::PhantomData,
-    mem,
-    num::NonZeroUsize,
-    pin::Pin,
-    rc::Rc,
-    sync::{
-        atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst},
-        Arc,
-    },
-    task::{Context, Poll},
-    time::Duration,
-};
-use util::TryFutureExt;
-use waker_fn::waker_fn;
-
-#[cfg(any(test, feature = "test-support"))]
-use rand::rngs::StdRng;
-
-#[derive(Clone)]
-pub struct BackgroundExecutor {
-    dispatcher: Arc<dyn PlatformDispatcher>,
-}
-
-#[derive(Clone)]
-pub struct ForegroundExecutor {
-    dispatcher: Arc<dyn PlatformDispatcher>,
-    not_send: PhantomData<Rc<()>>,
-}
-
-#[must_use]
-#[derive(Debug)]
-pub enum Task<T> {
-    Ready(Option<T>),
-    Spawned(async_task::Task<T>),
-}
-
-impl<T> Task<T> {
-    pub fn ready(val: T) -> Self {
-        Task::Ready(Some(val))
-    }
-
-    pub fn detach(self) {
-        match self {
-            Task::Ready(_) => {}
-            Task::Spawned(task) => task.detach(),
-        }
-    }
-}
-
-impl<E, T> Task<Result<T, E>>
-where
-    T: 'static,
-    E: 'static + Debug,
-{
-    #[track_caller]
-    pub fn detach_and_log_err(self, cx: &mut AppContext) {
-        let location = core::panic::Location::caller();
-        cx.foreground_executor()
-            .spawn(self.log_tracked_err(*location))
-            .detach();
-    }
-}
-
-impl<T> Future for Task<T> {
-    type Output = T;
-
-    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
-        match unsafe { self.get_unchecked_mut() } {
-            Task::Ready(val) => Poll::Ready(val.take().unwrap()),
-            Task::Spawned(task) => task.poll(cx),
-        }
-    }
-}
-
-#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
-pub struct TaskLabel(NonZeroUsize);
-
-impl Default for TaskLabel {
-    fn default() -> Self {
-        Self::new()
-    }
-}
-
-impl TaskLabel {
-    pub fn new() -> Self {
-        static NEXT_TASK_LABEL: AtomicUsize = AtomicUsize::new(1);
-        Self(NEXT_TASK_LABEL.fetch_add(1, SeqCst).try_into().unwrap())
-    }
-}
-
-type AnyLocalFuture<R> = Pin<Box<dyn 'static + Future<Output = R>>>;
-
-type AnyFuture<R> = Pin<Box<dyn 'static + Send + Future<Output = R>>>;
-
-impl BackgroundExecutor {
-    pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
-        Self { dispatcher }
-    }
-
-    /// Enqueues the given future to be run to completion on a background thread.
-    pub fn spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
-    where
-        R: Send + 'static,
-    {
-        self.spawn_internal::<R>(Box::pin(future), None)
-    }
-
-    /// Enqueues the given future to be run to completion on a background thread.
-    /// The given label can be used to control the priority of the task in tests.
-    pub fn spawn_labeled<R>(
-        &self,
-        label: TaskLabel,
-        future: impl Future<Output = R> + Send + 'static,
-    ) -> Task<R>
-    where
-        R: Send + 'static,
-    {
-        self.spawn_internal::<R>(Box::pin(future), Some(label))
-    }
-
-    fn spawn_internal<R: Send + 'static>(
-        &self,
-        future: AnyFuture<R>,
-        label: Option<TaskLabel>,
-    ) -> Task<R> {
-        let dispatcher = self.dispatcher.clone();
-        let (runnable, task) =
-            async_task::spawn(future, move |runnable| dispatcher.dispatch(runnable, label));
-        runnable.schedule();
-        Task::Spawned(task)
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    #[track_caller]
-    pub fn block_test<R>(&self, future: impl Future<Output = R>) -> R {
-        if let Ok(value) = self.block_internal(false, future, usize::MAX) {
-            value
-        } else {
-            unreachable!()
-        }
-    }
-
-    pub fn block<R>(&self, future: impl Future<Output = R>) -> R {
-        if let Ok(value) = self.block_internal(true, future, usize::MAX) {
-            value
-        } else {
-            unreachable!()
-        }
-    }
-
-    #[track_caller]
-    pub(crate) fn block_internal<R>(
-        &self,
-        background_only: bool,
-        future: impl Future<Output = R>,
-        mut max_ticks: usize,
-    ) -> Result<R, ()> {
-        pin_mut!(future);
-        let unparker = self.dispatcher.unparker();
-        let awoken = Arc::new(AtomicBool::new(false));
-
-        let waker = waker_fn({
-            let awoken = awoken.clone();
-            move || {
-                awoken.store(true, SeqCst);
-                unparker.unpark();
-            }
-        });
-        let mut cx = std::task::Context::from_waker(&waker);
-
-        loop {
-            match future.as_mut().poll(&mut cx) {
-                Poll::Ready(result) => return Ok(result),
-                Poll::Pending => {
-                    if max_ticks == 0 {
-                        return Err(());
-                    }
-                    max_ticks -= 1;
-
-                    if !self.dispatcher.tick(background_only) {
-                        if awoken.swap(false, SeqCst) {
-                            continue;
-                        }
-
-                        #[cfg(any(test, feature = "test-support"))]
-                        if let Some(test) = self.dispatcher.as_test() {
-                            if !test.parking_allowed() {
-                                let mut backtrace_message = String::new();
-                                if let Some(backtrace) = test.waiting_backtrace() {
-                                    backtrace_message =
-                                        format!("\nbacktrace of waiting future:\n{:?}", backtrace);
-                                }
-                                panic!("parked with nothing left to run\n{:?}", backtrace_message)
-                            }
-                        }
-
-                        self.dispatcher.park();
-                    }
-                }
-            }
-        }
-    }
-
-    pub fn block_with_timeout<R>(
-        &self,
-        duration: Duration,
-        future: impl Future<Output = R>,
-    ) -> Result<R, impl Future<Output = R>> {
-        let mut future = Box::pin(future.fuse());
-        if duration.is_zero() {
-            return Err(future);
-        }
-
-        #[cfg(any(test, feature = "test-support"))]
-        let max_ticks = self
-            .dispatcher
-            .as_test()
-            .map_or(usize::MAX, |dispatcher| dispatcher.gen_block_on_ticks());
-        #[cfg(not(any(test, feature = "test-support")))]
-        let max_ticks = usize::MAX;
-
-        let mut timer = self.timer(duration).fuse();
-
-        let timeout = async {
-            futures::select_biased! {
-                value = future => Ok(value),
-                _ = timer => Err(()),
-            }
-        };
-        match self.block_internal(true, timeout, max_ticks) {
-            Ok(Ok(value)) => Ok(value),
-            _ => Err(future),
-        }
-    }
-
-    pub async fn scoped<'scope, F>(&self, scheduler: F)
-    where
-        F: FnOnce(&mut Scope<'scope>),
-    {
-        let mut scope = Scope::new(self.clone());
-        (scheduler)(&mut scope);
-        let spawned = mem::take(&mut scope.futures)
-            .into_iter()
-            .map(|f| self.spawn(f))
-            .collect::<Vec<_>>();
-        for task in spawned {
-            task.await;
-        }
-    }
-
-    pub fn timer(&self, duration: Duration) -> Task<()> {
-        let (runnable, task) = async_task::spawn(async move {}, {
-            let dispatcher = self.dispatcher.clone();
-            move |runnable| dispatcher.dispatch_after(duration, runnable)
-        });
-        runnable.schedule();
-        Task::Spawned(task)
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn start_waiting(&self) {
-        self.dispatcher.as_test().unwrap().start_waiting();
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn finish_waiting(&self) {
-        self.dispatcher.as_test().unwrap().finish_waiting();
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn simulate_random_delay(&self) -> impl Future<Output = ()> {
-        self.dispatcher.as_test().unwrap().simulate_random_delay()
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn deprioritize(&self, task_label: TaskLabel) {
-        self.dispatcher.as_test().unwrap().deprioritize(task_label)
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn advance_clock(&self, duration: Duration) {
-        self.dispatcher.as_test().unwrap().advance_clock(duration)
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn tick(&self) -> bool {
-        self.dispatcher.as_test().unwrap().tick(false)
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn run_until_parked(&self) {
-        self.dispatcher.as_test().unwrap().run_until_parked()
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn allow_parking(&self) {
-        self.dispatcher.as_test().unwrap().allow_parking();
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn rng(&self) -> StdRng {
-        self.dispatcher.as_test().unwrap().rng()
-    }
-
-    pub fn num_cpus(&self) -> usize {
-        num_cpus::get()
-    }
-
-    pub fn is_main_thread(&self) -> bool {
-        self.dispatcher.is_main_thread()
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn set_block_on_ticks(&self, range: std::ops::RangeInclusive<usize>) {
-        self.dispatcher.as_test().unwrap().set_block_on_ticks(range);
-    }
-}
-
-impl ForegroundExecutor {
-    pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
-        Self {
-            dispatcher,
-            not_send: PhantomData,
-        }
-    }
-
-    /// Enqueues the given closure to be run on any thread. The closure returns
-    /// a future which will be run to completion on any available thread.
-    pub fn spawn<R>(&self, future: impl Future<Output = R> + 'static) -> Task<R>
-    where
-        R: 'static,
-    {
-        let dispatcher = self.dispatcher.clone();
-        fn inner<R: 'static>(
-            dispatcher: Arc<dyn PlatformDispatcher>,
-            future: AnyLocalFuture<R>,
-        ) -> Task<R> {
-            let (runnable, task) = async_task::spawn_local(future, move |runnable| {
-                dispatcher.dispatch_on_main_thread(runnable)
-            });
-            runnable.schedule();
-            Task::Spawned(task)
-        }
-        inner::<R>(dispatcher, Box::pin(future))
-    }
-}
-
-pub struct Scope<'a> {
-    executor: BackgroundExecutor,
-    futures: Vec<Pin<Box<dyn Future<Output = ()> + Send + 'static>>>,
-    tx: Option<mpsc::Sender<()>>,
-    rx: mpsc::Receiver<()>,
-    lifetime: PhantomData<&'a ()>,
-}
-
-impl<'a> Scope<'a> {
-    fn new(executor: BackgroundExecutor) -> Self {
-        let (tx, rx) = mpsc::channel(1);
-        Self {
-            executor,
-            tx: Some(tx),
-            rx,
-            futures: Default::default(),
-            lifetime: PhantomData,
-        }
-    }
-
-    pub fn spawn<F>(&mut self, f: F)
-    where
-        F: Future<Output = ()> + Send + 'a,
-    {
-        let tx = self.tx.clone().unwrap();
-
-        // Safety: The 'a lifetime is guaranteed to outlive any of these futures because
-        // dropping this `Scope` blocks until all of the futures have resolved.
-        let f = unsafe {
-            mem::transmute::<
-                Pin<Box<dyn Future<Output = ()> + Send + 'a>>,
-                Pin<Box<dyn Future<Output = ()> + Send + 'static>>,
-            >(Box::pin(async move {
-                f.await;
-                drop(tx);
-            }))
-        };
-        self.futures.push(f);
-    }
-}
-
-impl<'a> Drop for Scope<'a> {
-    fn drop(&mut self) {
-        self.tx.take().unwrap();
-
-        // Wait until the channel is closed, which means that all of the spawned
-        // futures have resolved.
-        self.executor.block(self.rx.next());
-    }
-}

crates/gpui2/src/geometry.rs 🔗

@@ -1,2796 +0,0 @@
-use core::fmt::Debug;
-use derive_more::{Add, AddAssign, Div, DivAssign, Mul, Neg, Sub, SubAssign};
-use refineable::Refineable;
-use serde_derive::{Deserialize, Serialize};
-use std::{
-    cmp::{self, PartialOrd},
-    fmt,
-    ops::{Add, Div, Mul, MulAssign, Sub},
-};
-
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub enum Axis {
-    Vertical,
-    Horizontal,
-}
-
-impl Axis {
-    pub fn invert(&self) -> Self {
-        match self {
-            Axis::Vertical => Axis::Horizontal,
-            Axis::Horizontal => Axis::Vertical,
-        }
-    }
-}
-
-pub trait Along {
-    type Unit;
-
-    fn along(&self, axis: Axis) -> Self::Unit;
-
-    fn apply_along(&self, axis: Axis, f: impl FnOnce(Self::Unit) -> Self::Unit) -> Self;
-}
-
-impl sqlez::bindable::StaticColumnCount for Axis {}
-impl sqlez::bindable::Bind for Axis {
-    fn bind(
-        &self,
-        statement: &sqlez::statement::Statement,
-        start_index: i32,
-    ) -> anyhow::Result<i32> {
-        match self {
-            Axis::Horizontal => "Horizontal",
-            Axis::Vertical => "Vertical",
-        }
-        .bind(statement, start_index)
-    }
-}
-
-impl sqlez::bindable::Column for Axis {
-    fn column(
-        statement: &mut sqlez::statement::Statement,
-        start_index: i32,
-    ) -> anyhow::Result<(Self, i32)> {
-        String::column(statement, start_index).and_then(|(axis_text, next_index)| {
-            Ok((
-                match axis_text.as_str() {
-                    "Horizontal" => Axis::Horizontal,
-                    "Vertical" => Axis::Vertical,
-                    _ => anyhow::bail!("Stored serialized item kind is incorrect"),
-                },
-                next_index,
-            ))
-        })
-    }
-}
-
-/// Describes a location in a 2D cartesian coordinate space.
-///
-/// It holds two public fields, `x` and `y`, which represent the coordinates in the space.
-/// The type `T` for the coordinates can be any type that implements `Default`, `Clone`, and `Debug`.
-///
-/// # Examples
-///
-/// ```
-/// # use zed::Point;
-/// let point = Point { x: 10, y: 20 };
-/// println!("{:?}", point); // Outputs: Point { x: 10, y: 20 }
-/// ```
-#[derive(Refineable, Default, Add, AddAssign, Sub, SubAssign, Copy, Debug, PartialEq, Eq, Hash)]
-#[refineable(Debug)]
-#[repr(C)]
-pub struct Point<T: Default + Clone + Debug> {
-    pub x: T,
-    pub y: T,
-}
-
-/// Constructs a new `Point<T>` with the given x and y coordinates.
-///
-/// # Arguments
-///
-/// * `x` - The x coordinate of the point.
-/// * `y` - The y coordinate of the point.
-///
-/// # Returns
-///
-/// Returns a `Point<T>` with the specified coordinates.
-///
-/// # Examples
-///
-/// ```
-/// # use zed::Point;
-/// let p = point(10, 20);
-/// assert_eq!(p.x, 10);
-/// assert_eq!(p.y, 20);
-/// ```
-pub fn point<T: Clone + Debug + Default>(x: T, y: T) -> Point<T> {
-    Point { x, y }
-}
-
-impl<T: Clone + Debug + Default> Point<T> {
-    /// Creates a new `Point` with the specified `x` and `y` coordinates.
-    ///
-    /// # Arguments
-    ///
-    /// * `x` - The horizontal coordinate of the point.
-    /// * `y` - The vertical coordinate of the point.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// let p = Point::new(10, 20);
-    /// assert_eq!(p.x, 10);
-    /// assert_eq!(p.y, 20);
-    /// ```
-    pub const fn new(x: T, y: T) -> Self {
-        Self { x, y }
-    }
-
-    /// Transforms the point to a `Point<U>` by applying the given function to both coordinates.
-    ///
-    /// This method allows for converting a `Point<T>` to a `Point<U>` by specifying a closure
-    /// that defines how to convert between the two types. The closure is applied to both the `x`
-    /// and `y` coordinates, resulting in a new point of the desired type.
-    ///
-    /// # Arguments
-    ///
-    /// * `f` - A closure that takes a value of type `T` and returns a value of type `U`.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::Point;
-    /// let p = Point { x: 3, y: 4 };
-    /// let p_float = p.map(|coord| coord as f32);
-    /// assert_eq!(p_float, Point { x: 3.0, y: 4.0 });
-    /// ```
-    pub fn map<U: Clone + Default + Debug>(&self, f: impl Fn(T) -> U) -> Point<U> {
-        Point {
-            x: f(self.x.clone()),
-            y: f(self.y.clone()),
-        }
-    }
-}
-
-impl<T: Clone + Debug + Default> Along for Point<T> {
-    type Unit = T;
-
-    fn along(&self, axis: Axis) -> T {
-        match axis {
-            Axis::Horizontal => self.x.clone(),
-            Axis::Vertical => self.y.clone(),
-        }
-    }
-
-    fn apply_along(&self, axis: Axis, f: impl FnOnce(T) -> T) -> Point<T> {
-        match axis {
-            Axis::Horizontal => Point {
-                x: f(self.x.clone()),
-                y: self.y.clone(),
-            },
-            Axis::Vertical => Point {
-                x: self.x.clone(),
-                y: f(self.y.clone()),
-            },
-        }
-    }
-}
-
-impl Point<Pixels> {
-    /// Scales the point by a given factor, which is typically derived from the resolution
-    /// of a target display to ensure proper sizing of UI elements.
-    ///
-    /// # Arguments
-    ///
-    /// * `factor` - The scaling factor to apply to both the x and y coordinates.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::{Point, Pixels, ScaledPixels};
-    /// let p = Point { x: Pixels(10.0), y: Pixels(20.0) };
-    /// let scaled_p = p.scale(1.5);
-    /// assert_eq!(scaled_p, Point { x: ScaledPixels(15.0), y: ScaledPixels(30.0) });
-    /// ```
-    pub fn scale(&self, factor: f32) -> Point<ScaledPixels> {
-        Point {
-            x: self.x.scale(factor),
-            y: self.y.scale(factor),
-        }
-    }
-
-    /// Calculates the Euclidean distance from the origin (0, 0) to this point.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::Point;
-    /// # use zed::Pixels;
-    /// let p = Point { x: Pixels(3.0), y: Pixels(4.0) };
-    /// assert_eq!(p.magnitude(), 5.0);
-    /// ```
-    pub fn magnitude(&self) -> f64 {
-        ((self.x.0.powi(2) + self.y.0.powi(2)) as f64).sqrt()
-    }
-}
-
-impl<T, Rhs> Mul<Rhs> for Point<T>
-where
-    T: Mul<Rhs, Output = T> + Clone + Default + Debug,
-    Rhs: Clone + Debug,
-{
-    type Output = Point<T>;
-
-    fn mul(self, rhs: Rhs) -> Self::Output {
-        Point {
-            x: self.x * rhs.clone(),
-            y: self.y * rhs,
-        }
-    }
-}
-
-impl<T, S> MulAssign<S> for Point<T>
-where
-    T: Clone + Mul<S, Output = T> + Default + Debug,
-    S: Clone,
-{
-    fn mul_assign(&mut self, rhs: S) {
-        self.x = self.x.clone() * rhs.clone();
-        self.y = self.y.clone() * rhs;
-    }
-}
-
-impl<T, S> Div<S> for Point<T>
-where
-    T: Div<S, Output = T> + Clone + Default + Debug,
-    S: Clone,
-{
-    type Output = Self;
-
-    fn div(self, rhs: S) -> Self::Output {
-        Self {
-            x: self.x / rhs.clone(),
-            y: self.y / rhs,
-        }
-    }
-}
-
-impl<T> Point<T>
-where
-    T: PartialOrd + Clone + Default + Debug,
-{
-    /// Returns a new point with the maximum values of each dimension from `self` and `other`.
-    ///
-    /// # Arguments
-    ///
-    /// * `other` - A reference to another `Point` to compare with `self`.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::Point;
-    /// let p1 = Point { x: 3, y: 7 };
-    /// let p2 = Point { x: 5, y: 2 };
-    /// let max_point = p1.max(&p2);
-    /// assert_eq!(max_point, Point { x: 5, y: 7 });
-    /// ```
-    pub fn max(&self, other: &Self) -> Self {
-        Point {
-            x: if self.x > other.x {
-                self.x.clone()
-            } else {
-                other.x.clone()
-            },
-            y: if self.y > other.y {
-                self.y.clone()
-            } else {
-                other.y.clone()
-            },
-        }
-    }
-
-    /// Returns a new point with the minimum values of each dimension from `self` and `other`.
-    ///
-    /// # Arguments
-    ///
-    /// * `other` - A reference to another `Point` to compare with `self`.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::Point;
-    /// let p1 = Point { x: 3, y: 7 };
-    /// let p2 = Point { x: 5, y: 2 };
-    /// let min_point = p1.min(&p2);
-    /// assert_eq!(min_point, Point { x: 3, y: 2 });
-    /// ```
-    pub fn min(&self, other: &Self) -> Self {
-        Point {
-            x: if self.x <= other.x {
-                self.x.clone()
-            } else {
-                other.x.clone()
-            },
-            y: if self.y <= other.y {
-                self.y.clone()
-            } else {
-                other.y.clone()
-            },
-        }
-    }
-
-    /// Clamps the point to a specified range.
-    ///
-    /// Given a minimum point and a maximum point, this method constrains the current point
-    /// such that its coordinates do not exceed the range defined by the minimum and maximum points.
-    /// If the current point's coordinates are less than the minimum, they are set to the minimum.
-    /// If they are greater than the maximum, they are set to the maximum.
-    ///
-    /// # Arguments
-    ///
-    /// * `min` - A reference to a `Point` representing the minimum allowable coordinates.
-    /// * `max` - A reference to a `Point` representing the maximum allowable coordinates.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::Point;
-    /// let p = Point { x: 10, y: 20 };
-    /// let min = Point { x: 0, y: 5 };
-    /// let max = Point { x: 15, y: 25 };
-    /// let clamped_p = p.clamp(&min, &max);
-    /// assert_eq!(clamped_p, Point { x: 10, y: 20 });
-    ///
-    /// let p_out_of_bounds = Point { x: -5, y: 30 };
-    /// let clamped_p_out_of_bounds = p_out_of_bounds.clamp(&min, &max);
-    /// assert_eq!(clamped_p_out_of_bounds, Point { x: 0, y: 25 });
-    /// ```
-    pub fn clamp(&self, min: &Self, max: &Self) -> Self {
-        self.max(min).min(max)
-    }
-}
-
-impl<T: Clone + Default + Debug> Clone for Point<T> {
-    fn clone(&self) -> Self {
-        Self {
-            x: self.x.clone(),
-            y: self.y.clone(),
-        }
-    }
-}
-
-/// A structure representing a two-dimensional size with width and height in a given unit.
-///
-/// This struct is generic over the type `T`, which can be any type that implements `Clone`, `Default`, and `Debug`.
-/// It is commonly used to specify dimensions for elements in a UI, such as a window or element.
-#[derive(Refineable, Default, Clone, Copy, PartialEq, Div, Hash, Serialize, Deserialize)]
-#[refineable(Debug)]
-#[repr(C)]
-pub struct Size<T: Clone + Default + Debug> {
-    pub width: T,
-    pub height: T,
-}
-
-/// Constructs a new `Size<T>` with the provided width and height.
-///
-/// # Arguments
-///
-/// * `width` - The width component of the `Size`.
-/// * `height` - The height component of the `Size`.
-///
-/// # Examples
-///
-/// ```
-/// # use zed::Size;
-/// let my_size = size(10, 20);
-/// assert_eq!(my_size.width, 10);
-/// assert_eq!(my_size.height, 20);
-/// ```
-pub fn size<T>(width: T, height: T) -> Size<T>
-where
-    T: Clone + Default + Debug,
-{
-    Size { width, height }
-}
-
-impl<T> Size<T>
-where
-    T: Clone + Default + Debug,
-{
-    /// Applies a function to the width and height of the size, producing a new `Size<U>`.
-    ///
-    /// This method allows for converting a `Size<T>` to a `Size<U>` by specifying a closure
-    /// that defines how to convert between the two types. The closure is applied to both the `width`
-    /// and `height`, resulting in a new size of the desired type.
-    ///
-    /// # Arguments
-    ///
-    /// * `f` - A closure that takes a value of type `T` and returns a value of type `U`.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::Size;
-    /// let my_size = Size { width: 10, height: 20 };
-    /// let my_new_size = my_size.map(|dimension| dimension as f32 * 1.5);
-    /// assert_eq!(my_new_size, Size { width: 15.0, height: 30.0 });
-    /// ```
-    pub fn map<U>(&self, f: impl Fn(T) -> U) -> Size<U>
-    where
-        U: Clone + Default + Debug,
-    {
-        Size {
-            width: f(self.width.clone()),
-            height: f(self.height.clone()),
-        }
-    }
-}
-
-impl Size<Pixels> {
-    /// Scales the size by a given factor.
-    ///
-    /// This method multiplies both the width and height by the provided scaling factor,
-    /// resulting in a new `Size<ScaledPixels>` that is proportionally larger or smaller
-    /// depending on the factor.
-    ///
-    /// # Arguments
-    ///
-    /// * `factor` - The scaling factor to apply to the width and height.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::{Size, Pixels, ScaledPixels};
-    /// let size = Size { width: Pixels(100.0), height: Pixels(50.0) };
-    /// let scaled_size = size.scale(2.0);
-    /// assert_eq!(scaled_size, Size { width: ScaledPixels(200.0), height: ScaledPixels(100.0) });
-    /// ```
-    pub fn scale(&self, factor: f32) -> Size<ScaledPixels> {
-        Size {
-            width: self.width.scale(factor),
-            height: self.height.scale(factor),
-        }
-    }
-}
-
-impl<T> Along for Size<T>
-where
-    T: Clone + Default + Debug,
-{
-    type Unit = T;
-
-    fn along(&self, axis: Axis) -> T {
-        match axis {
-            Axis::Horizontal => self.width.clone(),
-            Axis::Vertical => self.height.clone(),
-        }
-    }
-
-    /// Returns the value of this size along the given axis.
-    fn apply_along(&self, axis: Axis, f: impl FnOnce(T) -> T) -> Self {
-        match axis {
-            Axis::Horizontal => Size {
-                width: f(self.width.clone()),
-                height: self.height.clone(),
-            },
-            Axis::Vertical => Size {
-                width: self.width.clone(),
-                height: f(self.height.clone()),
-            },
-        }
-    }
-}
-
-impl<T> Size<T>
-where
-    T: PartialOrd + Clone + Default + Debug,
-{
-    /// Returns a new `Size` with the maximum width and height from `self` and `other`.
-    ///
-    /// # Arguments
-    ///
-    /// * `other` - A reference to another `Size` to compare with `self`.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::Size;
-    /// let size1 = Size { width: 30, height: 40 };
-    /// let size2 = Size { width: 50, height: 20 };
-    /// let max_size = size1.max(&size2);
-    /// assert_eq!(max_size, Size { width: 50, height: 40 });
-    /// ```
-    pub fn max(&self, other: &Self) -> Self {
-        Size {
-            width: if self.width >= other.width {
-                self.width.clone()
-            } else {
-                other.width.clone()
-            },
-            height: if self.height >= other.height {
-                self.height.clone()
-            } else {
-                other.height.clone()
-            },
-        }
-    }
-}
-
-impl<T> Sub for Size<T>
-where
-    T: Sub<Output = T> + Clone + Default + Debug,
-{
-    type Output = Size<T>;
-
-    fn sub(self, rhs: Self) -> Self::Output {
-        Size {
-            width: self.width - rhs.width,
-            height: self.height - rhs.height,
-        }
-    }
-}
-
-impl<T, Rhs> Mul<Rhs> for Size<T>
-where
-    T: Mul<Rhs, Output = Rhs> + Clone + Default + Debug,
-    Rhs: Clone + Default + Debug,
-{
-    type Output = Size<Rhs>;
-
-    fn mul(self, rhs: Rhs) -> Self::Output {
-        Size {
-            width: self.width * rhs.clone(),
-            height: self.height * rhs,
-        }
-    }
-}
-
-impl<T, S> MulAssign<S> for Size<T>
-where
-    T: Mul<S, Output = T> + Clone + Default + Debug,
-    S: Clone,
-{
-    fn mul_assign(&mut self, rhs: S) {
-        self.width = self.width.clone() * rhs.clone();
-        self.height = self.height.clone() * rhs;
-    }
-}
-
-impl<T> Eq for Size<T> where T: Eq + Default + Debug + Clone {}
-
-impl<T> Debug for Size<T>
-where
-    T: Clone + Default + Debug,
-{
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "Size {{ {:?} × {:?} }}", self.width, self.height)
-    }
-}
-
-impl<T: Clone + Default + Debug> From<Point<T>> for Size<T> {
-    fn from(point: Point<T>) -> Self {
-        Self {
-            width: point.x,
-            height: point.y,
-        }
-    }
-}
-
-impl From<Size<Pixels>> for Size<GlobalPixels> {
-    fn from(size: Size<Pixels>) -> Self {
-        Size {
-            width: GlobalPixels(size.width.0),
-            height: GlobalPixels(size.height.0),
-        }
-    }
-}
-
-impl From<Size<Pixels>> for Size<DefiniteLength> {
-    fn from(size: Size<Pixels>) -> Self {
-        Size {
-            width: size.width.into(),
-            height: size.height.into(),
-        }
-    }
-}
-
-impl From<Size<Pixels>> for Size<AbsoluteLength> {
-    fn from(size: Size<Pixels>) -> Self {
-        Size {
-            width: size.width.into(),
-            height: size.height.into(),
-        }
-    }
-}
-
-impl Size<Length> {
-    /// Returns a `Size` with both width and height set to fill the available space.
-    ///
-    /// This function creates a `Size` instance where both the width and height are set to `Length::Definite(DefiniteLength::Fraction(1.0))`,
-    /// which represents 100% of the available space in both dimensions.
-    ///
-    /// # Returns
-    ///
-    /// A `Size<Length>` that will fill the available space when used in a layout.
-    pub fn full() -> Self {
-        Self {
-            width: relative(1.).into(),
-            height: relative(1.).into(),
-        }
-    }
-}
-
-impl Size<Length> {
-    /// Returns a `Size` with both width and height set to `auto`, which allows the layout engine to determine the size.
-    ///
-    /// This function creates a `Size` instance where both the width and height are set to `Length::Auto`,
-    /// indicating that their size should be computed based on the layout context, such as the content size or
-    /// available space.
-    ///
-    /// # Returns
-    ///
-    /// A `Size<Length>` with width and height set to `Length::Auto`.
-    pub fn auto() -> Self {
-        Self {
-            width: Length::Auto,
-            height: Length::Auto,
-        }
-    }
-}
-
-/// Represents a rectangular area in a 2D space with an origin point and a size.
-///
-/// The `Bounds` struct is generic over a type `T` which represents the type of the coordinate system.
-/// The origin is represented as a `Point<T>` which defines the upper-left corner of the rectangle,
-/// and the size is represented as a `Size<T>` which defines the width and height of the rectangle.
-///
-/// # Examples
-///
-/// ```
-/// # use zed::{Bounds, Point, Size};
-/// let origin = Point { x: 0, y: 0 };
-/// let size = Size { width: 10, height: 20 };
-/// let bounds = Bounds::new(origin, size);
-///
-/// assert_eq!(bounds.origin, origin);
-/// assert_eq!(bounds.size, size);
-/// ```
-#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)]
-#[refineable(Debug)]
-#[repr(C)]
-pub struct Bounds<T: Clone + Default + Debug> {
-    pub origin: Point<T>,
-    pub size: Size<T>,
-}
-
-impl<T> Bounds<T>
-where
-    T: Clone + Debug + Sub<Output = T> + Default,
-{
-    /// Constructs a `Bounds` from two corner points: the upper-left and lower-right corners.
-    ///
-    /// This function calculates the origin and size of the `Bounds` based on the provided corner points.
-    /// The origin is set to the upper-left corner, and the size is determined by the difference between
-    /// the x and y coordinates of the lower-right and upper-left points.
-    ///
-    /// # Arguments
-    ///
-    /// * `upper_left` - A `Point<T>` representing the upper-left corner of the rectangle.
-    /// * `lower_right` - A `Point<T>` representing the lower-right corner of the rectangle.
-    ///
-    /// # Returns
-    ///
-    /// Returns a `Bounds<T>` that encompasses the area defined by the two corner points.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::{Bounds, Point};
-    /// let upper_left = Point { x: 0, y: 0 };
-    /// let lower_right = Point { x: 10, y: 10 };
-    /// let bounds = Bounds::from_corners(upper_left, lower_right);
-    ///
-    /// assert_eq!(bounds.origin, upper_left);
-    /// assert_eq!(bounds.size.width, 10);
-    /// assert_eq!(bounds.size.height, 10);
-    /// ```
-    pub fn from_corners(upper_left: Point<T>, lower_right: Point<T>) -> Self {
-        let origin = Point {
-            x: upper_left.x.clone(),
-            y: upper_left.y.clone(),
-        };
-        let size = Size {
-            width: lower_right.x - upper_left.x,
-            height: lower_right.y - upper_left.y,
-        };
-        Bounds { origin, size }
-    }
-
-    /// Creates a new `Bounds` with the specified origin and size.
-    ///
-    /// # Arguments
-    ///
-    /// * `origin` - A `Point<T>` representing the origin of the bounds.
-    /// * `size` - A `Size<T>` representing the size of the bounds.
-    ///
-    /// # Returns
-    ///
-    /// Returns a `Bounds<T>` that has the given origin and size.
-    pub fn new(origin: Point<T>, size: Size<T>) -> Self {
-        Bounds { origin, size }
-    }
-}
-
-impl<T> Bounds<T>
-where
-    T: Clone + Debug + PartialOrd + Add<T, Output = T> + Sub<Output = T> + Default + Half,
-{
-    /// Checks if this `Bounds` intersects with another `Bounds`.
-    ///
-    /// Two `Bounds` instances intersect if they overlap in the 2D space they occupy.
-    /// This method checks if there is any overlapping area between the two bounds.
-    ///
-    /// # Arguments
-    ///
-    /// * `other` - A reference to another `Bounds` to check for intersection with.
-    ///
-    /// # Returns
-    ///
-    /// Returns `true` if there is any intersection between the two bounds, `false` otherwise.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::{Bounds, Point, Size};
-    /// let bounds1 = Bounds {
-    ///     origin: Point { x: 0, y: 0 },
-    ///     size: Size { width: 10, height: 10 },
-    /// };
-    /// let bounds2 = Bounds {
-    ///     origin: Point { x: 5, y: 5 },
-    ///     size: Size { width: 10, height: 10 },
-    /// };
-    /// let bounds3 = Bounds {
-    ///     origin: Point { x: 20, y: 20 },
-    ///     size: Size { width: 10, height: 10 },
-    /// };
-    ///
-    /// assert_eq!(bounds1.intersects(&bounds2), true); // Overlapping bounds
-    /// assert_eq!(bounds1.intersects(&bounds3), false); // Non-overlapping bounds
-    /// ```
-    pub fn intersects(&self, other: &Bounds<T>) -> bool {
-        let my_lower_right = self.lower_right();
-        let their_lower_right = other.lower_right();
-
-        self.origin.x < their_lower_right.x
-            && my_lower_right.x > other.origin.x
-            && self.origin.y < their_lower_right.y
-            && my_lower_right.y > other.origin.y
-    }
-
-    /// Dilates the bounds by a specified amount in all directions.
-    ///
-    /// This method expands the bounds by the given `amount`, increasing the size
-    /// and adjusting the origin so that the bounds grow outwards equally in all directions.
-    /// The resulting bounds will have its width and height increased by twice the `amount`
-    /// (since it grows in both directions), and the origin will be moved by `-amount`
-    /// in both the x and y directions.
-    ///
-    /// # Arguments
-    ///
-    /// * `amount` - The amount by which to dilate the bounds.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::{Bounds, Point, Size};
-    /// let mut bounds = Bounds {
-    ///     origin: Point { x: 10, y: 10 },
-    ///     size: Size { width: 10, height: 10 },
-    /// };
-    /// bounds.dilate(5);
-    /// assert_eq!(bounds, Bounds {
-    ///     origin: Point { x: 5, y: 5 },
-    ///     size: Size { width: 20, height: 20 },
-    /// });
-    /// ```
-    pub fn dilate(&mut self, amount: T) {
-        self.origin.x = self.origin.x.clone() - amount.clone();
-        self.origin.y = self.origin.y.clone() - amount.clone();
-        let double_amount = amount.clone() + amount;
-        self.size.width = self.size.width.clone() + double_amount.clone();
-        self.size.height = self.size.height.clone() + double_amount;
-    }
-
-    /// Returns the center point of the bounds.
-    ///
-    /// Calculates the center by taking the origin's x and y coordinates and adding half the width and height
-    /// of the bounds, respectively. The center is represented as a `Point<T>` where `T` is the type of the
-    /// coordinate system.
-    ///
-    /// # Returns
-    ///
-    /// A `Point<T>` representing the center of the bounds.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::{Bounds, Point, Size};
-    /// let bounds = Bounds {
-    ///     origin: Point { x: 0, y: 0 },
-    ///     size: Size { width: 10, height: 20 },
-    /// };
-    /// let center = bounds.center();
-    /// assert_eq!(center, Point { x: 5, y: 10 });
-    /// ```
-    pub fn center(&self) -> Point<T> {
-        Point {
-            x: self.origin.x.clone() + self.size.width.clone().half(),
-            y: self.origin.y.clone() + self.size.height.clone().half(),
-        }
-    }
-}
-
-impl<T: Clone + Default + Debug + PartialOrd + Add<T, Output = T> + Sub<Output = T>> Bounds<T> {
-    /// Calculates the intersection of two `Bounds` objects.
-    ///
-    /// This method computes the overlapping region of two `Bounds`. If the bounds do not intersect,
-    /// the resulting `Bounds` will have a size with width and height of zero.
-    ///
-    /// # Arguments
-    ///
-    /// * `other` - A reference to another `Bounds` to intersect with.
-    ///
-    /// # Returns
-    ///
-    /// Returns a `Bounds` representing the intersection area. If there is no intersection,
-    /// the returned `Bounds` will have a size with width and height of zero.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::{Bounds, Point, Size};
-    /// let bounds1 = Bounds {
-    ///     origin: Point { x: 0, y: 0 },
-    ///     size: Size { width: 10, height: 10 },
-    /// };
-    /// let bounds2 = Bounds {
-    ///     origin: Point { x: 5, y: 5 },
-    ///     size: Size { width: 10, height: 10 },
-    /// };
-    /// let intersection = bounds1.intersect(&bounds2);
-    ///
-    /// assert_eq!(intersection, Bounds {
-    ///     origin: Point { x: 5, y: 5 },
-    ///     size: Size { width: 5, height: 5 },
-    /// });
-    /// ```
-    pub fn intersect(&self, other: &Self) -> Self {
-        let upper_left = self.origin.max(&other.origin);
-        let lower_right = self.lower_right().min(&other.lower_right());
-        Self::from_corners(upper_left, lower_right)
-    }
-
-    /// Computes the union of two `Bounds`.
-    ///
-    /// This method calculates the smallest `Bounds` that contains both the current `Bounds` and the `other` `Bounds`.
-    /// The resulting `Bounds` will have an origin that is the minimum of the origins of the two `Bounds`,
-    /// and a size that encompasses the furthest extents of both `Bounds`.
-    ///
-    /// # Arguments
-    ///
-    /// * `other` - A reference to another `Bounds` to create a union with.
-    ///
-    /// # Returns
-    ///
-    /// Returns a `Bounds` representing the union of the two `Bounds`.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::{Bounds, Point, Size};
-    /// let bounds1 = Bounds {
-    ///     origin: Point { x: 0, y: 0 },
-    ///     size: Size { width: 10, height: 10 },
-    /// };
-    /// let bounds2 = Bounds {
-    ///     origin: Point { x: 5, y: 5 },
-    ///     size: Size { width: 15, height: 15 },
-    /// };
-    /// let union_bounds = bounds1.union(&bounds2);
-    ///
-    /// assert_eq!(union_bounds, Bounds {
-    ///     origin: Point { x: 0, y: 0 },
-    ///     size: Size { width: 20, height: 20 },
-    /// });
-    /// ```
-    pub fn union(&self, other: &Self) -> Self {
-        let top_left = self.origin.min(&other.origin);
-        let bottom_right = self.lower_right().max(&other.lower_right());
-        Bounds::from_corners(top_left, bottom_right)
-    }
-}
-
-impl<T, Rhs> Mul<Rhs> for Bounds<T>
-where
-    T: Mul<Rhs, Output = Rhs> + Clone + Default + Debug,
-    Point<T>: Mul<Rhs, Output = Point<Rhs>>,
-    Rhs: Clone + Default + Debug,
-{
-    type Output = Bounds<Rhs>;
-
-    fn mul(self, rhs: Rhs) -> Self::Output {
-        Bounds {
-            origin: self.origin * rhs.clone(),
-            size: self.size * rhs,
-        }
-    }
-}
-
-impl<T, S> MulAssign<S> for Bounds<T>
-where
-    T: Mul<S, Output = T> + Clone + Default + Debug,
-    S: Clone,
-{
-    fn mul_assign(&mut self, rhs: S) {
-        self.origin *= rhs.clone();
-        self.size *= rhs;
-    }
-}
-
-impl<T, S> Div<S> for Bounds<T>
-where
-    Size<T>: Div<S, Output = Size<T>>,
-    T: Div<S, Output = T> + Default + Clone + Debug,
-    S: Clone,
-{
-    type Output = Self;
-
-    fn div(self, rhs: S) -> Self {
-        Self {
-            origin: self.origin / rhs.clone(),
-            size: self.size / rhs,
-        }
-    }
-}
-
-impl<T> Bounds<T>
-where
-    T: Add<T, Output = T> + Clone + Default + Debug,
-{
-    /// Returns the top edge of the bounds.
-    ///
-    /// # Returns
-    ///
-    /// A value of type `T` representing the y-coordinate of the top edge of the bounds.
-    pub fn top(&self) -> T {
-        self.origin.y.clone()
-    }
-
-    /// Returns the bottom edge of the bounds.
-    ///
-    /// # Returns
-    ///
-    /// A value of type `T` representing the y-coordinate of the bottom edge of the bounds.
-    pub fn bottom(&self) -> T {
-        self.origin.y.clone() + self.size.height.clone()
-    }
-
-    /// Returns the left edge of the bounds.
-    ///
-    /// # Returns
-    ///
-    /// A value of type `T` representing the x-coordinate of the left edge of the bounds.
-    pub fn left(&self) -> T {
-        self.origin.x.clone()
-    }
-
-    /// Returns the right edge of the bounds.
-    ///
-    /// # Returns
-    ///
-    /// A value of type `T` representing the x-coordinate of the right edge of the bounds.
-    pub fn right(&self) -> T {
-        self.origin.x.clone() + self.size.width.clone()
-    }
-
-    /// Returns the upper-right corner point of the bounds.
-    ///
-    /// # Returns
-    ///
-    /// A `Point<T>` representing the upper-right corner of the bounds.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::{Bounds, Point, Size};
-    /// let bounds = Bounds {
-    ///     origin: Point { x: 0, y: 0 },
-    ///     size: Size { width: 10, height: 20 },
-    /// };
-    /// let upper_right = bounds.upper_right();
-    /// assert_eq!(upper_right, Point { x: 10, y: 0 });
-    /// ```
-    pub fn upper_right(&self) -> Point<T> {
-        Point {
-            x: self.origin.x.clone() + self.size.width.clone(),
-            y: self.origin.y.clone(),
-        }
-    }
-
-    /// Returns the lower-right corner point of the bounds.
-    ///
-    /// # Returns
-    ///
-    /// A `Point<T>` representing the lower-right corner of the bounds.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::{Bounds, Point, Size};
-    /// let bounds = Bounds {
-    ///     origin: Point { x: 0, y: 0 },
-    ///     size: Size { width: 10, height: 20 },
-    /// };
-    /// let lower_right = bounds.lower_right();
-    /// assert_eq!(lower_right, Point { x: 10, y: 20 });
-    /// ```
-    pub fn lower_right(&self) -> Point<T> {
-        Point {
-            x: self.origin.x.clone() + self.size.width.clone(),
-            y: self.origin.y.clone() + self.size.height.clone(),
-        }
-    }
-
-    /// Returns the lower-left corner point of the bounds.
-    ///
-    /// # Returns
-    ///
-    /// A `Point<T>` representing the lower-left corner of the bounds.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::{Bounds, Point, Size};
-    /// let bounds = Bounds {
-    ///     origin: Point { x: 0, y: 0 },
-    ///     size: Size { width: 10, height: 20 },
-    /// };
-    /// let lower_left = bounds.lower_left();
-    /// assert_eq!(lower_left, Point { x: 0, y: 20 });
-    /// ```
-    pub fn lower_left(&self) -> Point<T> {
-        Point {
-            x: self.origin.x.clone(),
-            y: self.origin.y.clone() + self.size.height.clone(),
-        }
-    }
-}
-
-impl<T> Bounds<T>
-where
-    T: Add<T, Output = T> + PartialOrd + Clone + Default + Debug,
-{
-    /// Checks if the given point is within the bounds.
-    ///
-    /// This method determines whether a point lies inside the rectangle defined by the bounds,
-    /// including the edges. The point is considered inside if its x-coordinate is greater than
-    /// or equal to the left edge and less than or equal to the right edge, and its y-coordinate
-    /// is greater than or equal to the top edge and less than or equal to the bottom edge of the bounds.
-    ///
-    /// # Arguments
-    ///
-    /// * `point` - A reference to a `Point<T>` that represents the point to check.
-    ///
-    /// # Returns
-    ///
-    /// Returns `true` if the point is within the bounds, `false` otherwise.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::{Point, Bounds};
-    /// let bounds = Bounds {
-    ///     origin: Point { x: 0, y: 0 },
-    ///     size: Size { width: 10, height: 10 },
-    /// };
-    /// let inside_point = Point { x: 5, y: 5 };
-    /// let outside_point = Point { x: 15, y: 15 };
-    ///
-    /// assert!(bounds.contains_point(&inside_point));
-    /// assert!(!bounds.contains_point(&outside_point));
-    /// ```
-    pub fn contains(&self, point: &Point<T>) -> bool {
-        point.x >= self.origin.x
-            && point.x <= self.origin.x.clone() + self.size.width.clone()
-            && point.y >= self.origin.y
-            && point.y <= self.origin.y.clone() + self.size.height.clone()
-    }
-
-    /// Applies a function to the origin and size of the bounds, producing a new `Bounds<U>`.
-    ///
-    /// This method allows for converting a `Bounds<T>` to a `Bounds<U>` by specifying a closure
-    /// that defines how to convert between the two types. The closure is applied to the `origin` and
-    /// `size` fields, resulting in new bounds of the desired type.
-    ///
-    /// # Arguments
-    ///
-    /// * `f` - A closure that takes a value of type `T` and returns a value of type `U`.
-    ///
-    /// # Returns
-    ///
-    /// Returns a new `Bounds<U>` with the origin and size mapped by the provided function.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::{Bounds, Point, Size};
-    /// let bounds = Bounds {
-    ///     origin: Point { x: 10.0, y: 10.0 },
-    ///     size: Size { width: 10.0, height: 20.0 },
-    /// };
-    /// let new_bounds = bounds.map(|value| value as f64 * 1.5);
-    ///
-    /// assert_eq!(new_bounds, Bounds {
-    ///     origin: Point { x: 15.0, y: 15.0 },
-    ///     size: Size { width: 15.0, height: 30.0 },
-    /// });
-    pub fn map<U>(&self, f: impl Fn(T) -> U) -> Bounds<U>
-    where
-        U: Clone + Default + Debug,
-    {
-        Bounds {
-            origin: self.origin.map(&f),
-            size: self.size.map(f),
-        }
-    }
-}
-
-impl Bounds<Pixels> {
-    /// Scales the bounds by a given factor, typically used to adjust for display scaling.
-    ///
-    /// This method multiplies the origin and size of the bounds by the provided scaling factor,
-    /// resulting in a new `Bounds<ScaledPixels>` that is proportionally larger or smaller
-    /// depending on the scaling factor. This can be used to ensure that the bounds are properly
-    /// scaled for different display densities.
-    ///
-    /// # Arguments
-    ///
-    /// * `factor` - The scaling factor to apply to the origin and size, typically the display's scaling factor.
-    ///
-    /// # Returns
-    ///
-    /// Returns a new `Bounds<ScaledPixels>` that represents the scaled bounds.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::{Bounds, Point, Size, Pixels};
-    /// let bounds = Bounds {
-    ///     origin: Point { x: Pixels(10.0), y: Pixels(20.0) },
-    ///     size: Size { width: Pixels(30.0), height: Pixels(40.0) },
-    /// };
-    /// let display_scale_factor = 2.0;
-    /// let scaled_bounds = bounds.scale(display_scale_factor);
-    /// assert_eq!(scaled_bounds, Bounds {
-    ///     origin: Point { x: ScaledPixels(20.0), y: ScaledPixels(40.0) },
-    ///     size: Size { width: ScaledPixels(60.0), height: ScaledPixels(80.0) },
-    /// });
-    /// ```
-    pub fn scale(&self, factor: f32) -> Bounds<ScaledPixels> {
-        Bounds {
-            origin: self.origin.scale(factor),
-            size: self.size.scale(factor),
-        }
-    }
-}
-
-impl<T: Clone + Debug + Copy + Default> Copy for Bounds<T> {}
-
-/// Represents the edges of a box in a 2D space, such as padding or margin.
-///
-/// Each field represents the size of the edge on one side of the box: `top`, `right`, `bottom`, and `left`.
-///
-/// # Examples
-///
-/// ```
-/// # use zed::Edges;
-/// let edges = Edges {
-///     top: 10.0,
-///     right: 20.0,
-///     bottom: 30.0,
-///     left: 40.0,
-/// };
-///
-/// assert_eq!(edges.top, 10.0);
-/// assert_eq!(edges.right, 20.0);
-/// assert_eq!(edges.bottom, 30.0);
-/// assert_eq!(edges.left, 40.0);
-/// ```
-#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)]
-#[refineable(Debug)]
-#[repr(C)]
-pub struct Edges<T: Clone + Default + Debug> {
-    pub top: T,
-    pub right: T,
-    pub bottom: T,
-    pub left: T,
-}
-
-impl<T> Mul for Edges<T>
-where
-    T: Mul<Output = T> + Clone + Default + Debug,
-{
-    type Output = Self;
-
-    fn mul(self, rhs: Self) -> Self::Output {
-        Self {
-            top: self.top.clone() * rhs.top,
-            right: self.right.clone() * rhs.right,
-            bottom: self.bottom.clone() * rhs.bottom,
-            left: self.left.clone() * rhs.left,
-        }
-    }
-}
-
-impl<T, S> MulAssign<S> for Edges<T>
-where
-    T: Mul<S, Output = T> + Clone + Default + Debug,
-    S: Clone,
-{
-    fn mul_assign(&mut self, rhs: S) {
-        self.top = self.top.clone() * rhs.clone();
-        self.right = self.right.clone() * rhs.clone();
-        self.bottom = self.bottom.clone() * rhs.clone();
-        self.left = self.left.clone() * rhs;
-    }
-}
-
-impl<T: Clone + Default + Debug + Copy> Copy for Edges<T> {}
-
-impl<T: Clone + Default + Debug> Edges<T> {
-    /// Constructs `Edges` where all sides are set to the same specified value.
-    ///
-    /// This function creates an `Edges` instance with the `top`, `right`, `bottom`, and `left` fields all initialized
-    /// to the same value provided as an argument. This is useful when you want to have uniform edges around a box,
-    /// such as padding or margin with the same size on all sides.
-    ///
-    /// # Arguments
-    ///
-    /// * `value` - The value to set for all four sides of the edges.
-    ///
-    /// # Returns
-    ///
-    /// An `Edges` instance with all sides set to the given value.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::Edges;
-    /// let uniform_edges = Edges::all(10.0);
-    /// assert_eq!(uniform_edges.top, 10.0);
-    /// assert_eq!(uniform_edges.right, 10.0);
-    /// assert_eq!(uniform_edges.bottom, 10.0);
-    /// assert_eq!(uniform_edges.left, 10.0);
-    /// ```
-    pub fn all(value: T) -> Self {
-        Self {
-            top: value.clone(),
-            right: value.clone(),
-            bottom: value.clone(),
-            left: value,
-        }
-    }
-
-    /// Applies a function to each field of the `Edges`, producing a new `Edges<U>`.
-    ///
-    /// This method allows for converting an `Edges<T>` to an `Edges<U>` by specifying a closure
-    /// that defines how to convert between the two types. The closure is applied to each field
-    /// (`top`, `right`, `bottom`, `left`), resulting in new edges of the desired type.
-    ///
-    /// # Arguments
-    ///
-    /// * `f` - A closure that takes a reference to a value of type `T` and returns a value of type `U`.
-    ///
-    /// # Returns
-    ///
-    /// Returns a new `Edges<U>` with each field mapped by the provided function.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::Edges;
-    /// let edges = Edges { top: 10, right: 20, bottom: 30, left: 40 };
-    /// let edges_float = edges.map(|&value| value as f32 * 1.1);
-    /// assert_eq!(edges_float, Edges { top: 11.0, right: 22.0, bottom: 33.0, left: 44.0 });
-    /// ```
-    pub fn map<U>(&self, f: impl Fn(&T) -> U) -> Edges<U>
-    where
-        U: Clone + Default + Debug,
-    {
-        Edges {
-            top: f(&self.top),
-            right: f(&self.right),
-            bottom: f(&self.bottom),
-            left: f(&self.left),
-        }
-    }
-
-    /// Checks if any of the edges satisfy a given predicate.
-    ///
-    /// This method applies a predicate function to each field of the `Edges` and returns `true` if any field satisfies the predicate.
-    ///
-    /// # Arguments
-    ///
-    /// * `predicate` - A closure that takes a reference to a value of type `T` and returns a `bool`.
-    ///
-    /// # Returns
-    ///
-    /// Returns `true` if the predicate returns `true` for any of the edge values, `false` otherwise.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::Edges;
-    /// let edges = Edges {
-    ///     top: 10,
-    ///     right: 0,
-    ///     bottom: 5,
-    ///     left: 0,
-    /// };
-    ///
-    /// assert!(edges.any(|value| *value == 0));
-    /// assert!(edges.any(|value| *value > 0));
-    /// assert!(!edges.any(|value| *value > 10));
-    /// ```
-    pub fn any<F: Fn(&T) -> bool>(&self, predicate: F) -> bool {
-        predicate(&self.top)
-            || predicate(&self.right)
-            || predicate(&self.bottom)
-            || predicate(&self.left)
-    }
-}
-
-impl Edges<Length> {
-    /// Sets the edges of the `Edges` struct to `auto`, which is a special value that allows the layout engine to automatically determine the size of the edges.
-    ///
-    /// This is typically used in layout contexts where the exact size of the edges is not important, or when the size should be calculated based on the content or container.
-    ///
-    /// # Returns
-    ///
-    /// Returns an `Edges<Length>` with all edges set to `Length::Auto`.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::Edges;
-    /// let auto_edges = Edges::auto();
-    /// assert_eq!(auto_edges.top, Length::Auto);
-    /// assert_eq!(auto_edges.right, Length::Auto);
-    /// assert_eq!(auto_edges.bottom, Length::Auto);
-    /// assert_eq!(auto_edges.left, Length::Auto);
-    /// ```
-    pub fn auto() -> Self {
-        Self {
-            top: Length::Auto,
-            right: Length::Auto,
-            bottom: Length::Auto,
-            left: Length::Auto,
-        }
-    }
-
-    /// Sets the edges of the `Edges` struct to zero, which means no size or thickness.
-    ///
-    /// This is typically used when you want to specify that a box (like a padding or margin area)
-    /// should have no edges, effectively making it non-existent or invisible in layout calculations.
-    ///
-    /// # Returns
-    ///
-    /// Returns an `Edges<Length>` with all edges set to zero length.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::Edges;
-    /// let no_edges = Edges::zero();
-    /// assert_eq!(no_edges.top, Length::Definite(DefiniteLength::from(Pixels(0.))));
-    /// assert_eq!(no_edges.right, Length::Definite(DefiniteLength::from(Pixels(0.))));
-    /// assert_eq!(no_edges.bottom, Length::Definite(DefiniteLength::from(Pixels(0.))));
-    /// assert_eq!(no_edges.left, Length::Definite(DefiniteLength::from(Pixels(0.))));
-    /// ```
-    pub fn zero() -> Self {
-        Self {
-            top: px(0.).into(),
-            right: px(0.).into(),
-            bottom: px(0.).into(),
-            left: px(0.).into(),
-        }
-    }
-}
-
-impl Edges<DefiniteLength> {
-    /// Sets the edges of the `Edges` struct to zero, which means no size or thickness.
-    ///
-    /// This is typically used when you want to specify that a box (like a padding or margin area)
-    /// should have no edges, effectively making it non-existent or invisible in layout calculations.
-    ///
-    /// # Returns
-    ///
-    /// Returns an `Edges<DefiniteLength>` with all edges set to zero length.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::Edges;
-    /// let no_edges = Edges::zero();
-    /// assert_eq!(no_edges.top, DefiniteLength::from(zed::px(0.)));
-    /// assert_eq!(no_edges.right, DefiniteLength::from(zed::px(0.)));
-    /// assert_eq!(no_edges.bottom, DefiniteLength::from(zed::px(0.)));
-    /// assert_eq!(no_edges.left, DefiniteLength::from(zed::px(0.)));
-    /// ```
-    pub fn zero() -> Self {
-        Self {
-            top: px(0.).into(),
-            right: px(0.).into(),
-            bottom: px(0.).into(),
-            left: px(0.).into(),
-        }
-    }
-
-    /// Converts the `DefiniteLength` to `Pixels` based on the parent size and the REM size.
-    ///
-    /// This method allows for a `DefiniteLength` value to be converted into pixels, taking into account
-    /// the size of the parent element (for percentage-based lengths) and the size of a rem unit (for rem-based lengths).
-    ///
-    /// # Arguments
-    ///
-    /// * `parent_size` - `Size<AbsoluteLength>` representing the size of the parent element.
-    /// * `rem_size` - `Pixels` representing the size of one REM unit.
-    ///
-    /// # Returns
-    ///
-    /// Returns an `Edges<Pixels>` representing the edges with lengths converted to pixels.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::{Edges, DefiniteLength, px, AbsoluteLength, Size};
-    /// let edges = Edges {
-    ///     top: DefiniteLength::Absolute(AbsoluteLength::Pixels(px(10.0))),
-    ///     right: DefiniteLength::Fraction(0.5),
-    ///     bottom: DefiniteLength::Absolute(AbsoluteLength::Rems(rems(2.0))),
-    ///     left: DefiniteLength::Fraction(0.25),
-    /// };
-    /// let parent_size = Size {
-    ///     width: AbsoluteLength::Pixels(px(200.0)),
-    ///     height: AbsoluteLength::Pixels(px(100.0)),
-    /// };
-    /// let rem_size = px(16.0);
-    /// let edges_in_pixels = edges.to_pixels(parent_size, rem_size);
-    ///
-    /// assert_eq!(edges_in_pixels.top, px(10.0)); // Absolute length in pixels
-    /// assert_eq!(edges_in_pixels.right, px(100.0)); // 50% of parent width
-    /// assert_eq!(edges_in_pixels.bottom, px(32.0)); // 2 rems
-    /// assert_eq!(edges_in_pixels.left, px(50.0)); // 25% of parent width
-    /// ```
-    pub fn to_pixels(&self, parent_size: Size<AbsoluteLength>, rem_size: Pixels) -> Edges<Pixels> {
-        Edges {
-            top: self.top.to_pixels(parent_size.height, rem_size),
-            right: self.right.to_pixels(parent_size.width, rem_size),
-            bottom: self.bottom.to_pixels(parent_size.height, rem_size),
-            left: self.left.to_pixels(parent_size.width, rem_size),
-        }
-    }
-}
-
-impl Edges<AbsoluteLength> {
-    /// Sets the edges of the `Edges` struct to zero, which means no size or thickness.
-    ///
-    /// This is typically used when you want to specify that a box (like a padding or margin area)
-    /// should have no edges, effectively making it non-existent or invisible in layout calculations.
-    ///
-    /// # Returns
-    ///
-    /// Returns an `Edges<AbsoluteLength>` with all edges set to zero length.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::Edges;
-    /// let no_edges = Edges::zero();
-    /// assert_eq!(no_edges.top, AbsoluteLength::Pixels(Pixels(0.0)));
-    /// assert_eq!(no_edges.right, AbsoluteLength::Pixels(Pixels(0.0)));
-    /// assert_eq!(no_edges.bottom, AbsoluteLength::Pixels(Pixels(0.0)));
-    /// assert_eq!(no_edges.left, AbsoluteLength::Pixels(Pixels(0.0)));
-    /// ```
-    pub fn zero() -> Self {
-        Self {
-            top: px(0.).into(),
-            right: px(0.).into(),
-            bottom: px(0.).into(),
-            left: px(0.).into(),
-        }
-    }
-
-    /// Converts the `AbsoluteLength` to `Pixels` based on the `rem_size`.
-    ///
-    /// If the `AbsoluteLength` is already in pixels, it simply returns the corresponding `Pixels` value.
-    /// If the `AbsoluteLength` is in rems, it multiplies the number of rems by the `rem_size` to convert it to pixels.
-    ///
-    /// # Arguments
-    ///
-    /// * `rem_size` - The size of one rem unit in pixels.
-    ///
-    /// # Returns
-    ///
-    /// Returns an `Edges<Pixels>` representing the edges with lengths converted to pixels.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::{Edges, AbsoluteLength, Pixels, px};
-    /// let edges = Edges {
-    ///     top: AbsoluteLength::Pixels(px(10.0)),
-    ///     right: AbsoluteLength::Rems(rems(1.0)),
-    ///     bottom: AbsoluteLength::Pixels(px(20.0)),
-    ///     left: AbsoluteLength::Rems(rems(2.0)),
-    /// };
-    /// let rem_size = px(16.0);
-    /// let edges_in_pixels = edges.to_pixels(rem_size);
-    ///
-    /// assert_eq!(edges_in_pixels.top, px(10.0)); // Already in pixels
-    /// assert_eq!(edges_in_pixels.right, px(16.0)); // 1 rem converted to pixels
-    /// assert_eq!(edges_in_pixels.bottom, px(20.0)); // Already in pixels
-    /// assert_eq!(edges_in_pixels.left, px(32.0)); // 2 rems converted to pixels
-    /// ```
-    pub fn to_pixels(&self, rem_size: Pixels) -> Edges<Pixels> {
-        Edges {
-            top: self.top.to_pixels(rem_size),
-            right: self.right.to_pixels(rem_size),
-            bottom: self.bottom.to_pixels(rem_size),
-            left: self.left.to_pixels(rem_size),
-        }
-    }
-}
-
-impl Edges<Pixels> {
-    /// Scales the `Edges<Pixels>` by a given factor, returning `Edges<ScaledPixels>`.
-    ///
-    /// This method is typically used for adjusting the edge sizes for different display densities or scaling factors.
-    ///
-    /// # Arguments
-    ///
-    /// * `factor` - The scaling factor to apply to each edge.
-    ///
-    /// # Returns
-    ///
-    /// Returns a new `Edges<ScaledPixels>` where each edge is the result of scaling the original edge by the given factor.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::{Edges, Pixels};
-    /// let edges = Edges {
-    ///     top: Pixels(10.0),
-    ///     right: Pixels(20.0),
-    ///     bottom: Pixels(30.0),
-    ///     left: Pixels(40.0),
-    /// };
-    /// let scaled_edges = edges.scale(2.0);
-    /// assert_eq!(scaled_edges.top, ScaledPixels(20.0));
-    /// assert_eq!(scaled_edges.right, ScaledPixels(40.0));
-    /// assert_eq!(scaled_edges.bottom, ScaledPixels(60.0));
-    /// assert_eq!(scaled_edges.left, ScaledPixels(80.0));
-    /// ```
-    pub fn scale(&self, factor: f32) -> Edges<ScaledPixels> {
-        Edges {
-            top: self.top.scale(factor),
-            right: self.right.scale(factor),
-            bottom: self.bottom.scale(factor),
-            left: self.left.scale(factor),
-        }
-    }
-
-    /// Returns the maximum value of any edge.
-    ///
-    /// # Returns
-    ///
-    /// The maximum `Pixels` value among all four edges.
-    pub fn max(&self) -> Pixels {
-        self.top.max(self.right).max(self.bottom).max(self.left)
-    }
-}
-
-impl From<f32> for Edges<Pixels> {
-    fn from(val: f32) -> Self {
-        Edges {
-            top: val.into(),
-            right: val.into(),
-            bottom: val.into(),
-            left: val.into(),
-        }
-    }
-}
-
-/// Represents the corners of a box in a 2D space, such as border radius.
-///
-/// Each field represents the size of the corner on one side of the box: `top_left`, `top_right`, `bottom_right`, and `bottom_left`.
-/// ```
-#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)]
-#[refineable(Debug)]
-#[repr(C)]
-pub struct Corners<T: Clone + Default + Debug> {
-    pub top_left: T,
-    pub top_right: T,
-    pub bottom_right: T,
-    pub bottom_left: T,
-}
-
-impl<T> Corners<T>
-where
-    T: Clone + Default + Debug,
-{
-    /// Constructs `Corners` where all sides are set to the same specified value.
-    ///
-    /// This function creates a `Corners` instance with the `top_left`, `top_right`, `bottom_right`, and `bottom_left` fields all initialized
-    /// to the same value provided as an argument. This is useful when you want to have uniform corners around a box,
-    /// such as a uniform border radius on a rectangle.
-    ///
-    /// # Arguments
-    ///
-    /// * `value` - The value to set for all four corners.
-    ///
-    /// # Returns
-    ///
-    /// An `Corners` instance with all corners set to the given value.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::Corners;
-    /// let uniform_corners = Corners::all(5.0);
-    /// assert_eq!(uniform_corners.top_left, 5.0);
-    /// assert_eq!(uniform_corners.top_right, 5.0);
-    /// assert_eq!(uniform_corners.bottom_right, 5.0);
-    /// assert_eq!(uniform_corners.bottom_left, 5.0);
-    /// ```
-    pub fn all(value: T) -> Self {
-        Self {
-            top_left: value.clone(),
-            top_right: value.clone(),
-            bottom_right: value.clone(),
-            bottom_left: value,
-        }
-    }
-}
-
-impl Corners<AbsoluteLength> {
-    /// Converts the `AbsoluteLength` to `Pixels` based on the provided size and rem size, ensuring the resulting
-    /// `Pixels` do not exceed half of the maximum of the provided size's width and height.
-    ///
-    /// This method is particularly useful when dealing with corner radii, where the radius in pixels should not
-    /// exceed half the size of the box it applies to, to avoid the corners overlapping.
-    ///
-    /// # Arguments
-    ///
-    /// * `size` - The `Size<Pixels>` against which the maximum allowable radius is determined.
-    /// * `rem_size` - The size of one REM unit in pixels, used for conversion if the `AbsoluteLength` is in REMs.
-    ///
-    /// # Returns
-    ///
-    /// Returns a `Corners<Pixels>` instance with each corner's length converted to pixels and clamped to the
-    /// maximum allowable radius based on the provided size.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::{Corners, AbsoluteLength, Pixels, Size};
-    /// let corners = Corners {
-    ///     top_left: AbsoluteLength::Pixels(Pixels(15.0)),
-    ///     top_right: AbsoluteLength::Rems(Rems(1.0)),
-    ///     bottom_right: AbsoluteLength::Pixels(Pixels(20.0)),
-    ///     bottom_left: AbsoluteLength::Rems(Rems(2.0)),
-    /// };
-    /// let size = Size { width: Pixels(100.0), height: Pixels(50.0) };
-    /// let rem_size = Pixels(16.0);
-    /// let corners_in_pixels = corners.to_pixels(size, rem_size);
-    ///
-    /// // The resulting corners should not exceed half the size of the smallest dimension (50.0 / 2.0 = 25.0).
-    /// assert_eq!(corners_in_pixels.top_left, Pixels(15.0));
-    /// assert_eq!(corners_in_pixels.top_right, Pixels(16.0)); // 1 rem converted to pixels
-    /// assert_eq!(corners_in_pixels.bottom_right, Pixels(20.0).min(Pixels(25.0))); // Clamped to 25.0
-    /// assert_eq!(corners_in_pixels.bottom_left, Pixels(32.0).min(Pixels(25.0))); // 2 rems converted to pixels and clamped
-    /// ```
-    pub fn to_pixels(&self, size: Size<Pixels>, rem_size: Pixels) -> Corners<Pixels> {
-        let max = size.width.max(size.height) / 2.;
-        Corners {
-            top_left: self.top_left.to_pixels(rem_size).min(max),
-            top_right: self.top_right.to_pixels(rem_size).min(max),
-            bottom_right: self.bottom_right.to_pixels(rem_size).min(max),
-            bottom_left: self.bottom_left.to_pixels(rem_size).min(max),
-        }
-    }
-}
-
-impl Corners<Pixels> {
-    /// Scales the `Corners<Pixels>` by a given factor, returning `Corners<ScaledPixels>`.
-    ///
-    /// This method is typically used for adjusting the corner sizes for different display densities or scaling factors.
-    ///
-    /// # Arguments
-    ///
-    /// * `factor` - The scaling factor to apply to each corner.
-    ///
-    /// # Returns
-    ///
-    /// Returns a new `Corners<ScaledPixels>` where each corner is the result of scaling the original corner by the given factor.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::{Corners, Pixels};
-    /// let corners = Corners {
-    ///     top_left: Pixels(10.0),
-    ///     top_right: Pixels(20.0),
-    ///     bottom_right: Pixels(30.0),
-    ///     bottom_left: Pixels(40.0),
-    /// };
-    /// let scaled_corners = corners.scale(2.0);
-    /// assert_eq!(scaled_corners.top_left, ScaledPixels(20.0));
-    /// assert_eq!(scaled_corners.top_right, ScaledPixels(40.0));
-    /// assert_eq!(scaled_corners.bottom_right, ScaledPixels(60.0));
-    /// assert_eq!(scaled_corners.bottom_left, ScaledPixels(80.0));
-    /// ```
-    pub fn scale(&self, factor: f32) -> Corners<ScaledPixels> {
-        Corners {
-            top_left: self.top_left.scale(factor),
-            top_right: self.top_right.scale(factor),
-            bottom_right: self.bottom_right.scale(factor),
-            bottom_left: self.bottom_left.scale(factor),
-        }
-    }
-
-    /// Returns the maximum value of any corner.
-    ///
-    /// # Returns
-    ///
-    /// The maximum `Pixels` value among all four corners.
-    pub fn max(&self) -> Pixels {
-        self.top_left
-            .max(self.top_right)
-            .max(self.bottom_right)
-            .max(self.bottom_left)
-    }
-}
-
-impl<T: Clone + Default + Debug> Corners<T> {
-    /// Applies a function to each field of the `Corners`, producing a new `Corners<U>`.
-    ///
-    /// This method allows for converting a `Corners<T>` to a `Corners<U>` by specifying a closure
-    /// that defines how to convert between the two types. The closure is applied to each field
-    /// (`top_left`, `top_right`, `bottom_right`, `bottom_left`), resulting in new corners of the desired type.
-    ///
-    /// # Arguments
-    ///
-    /// * `f` - A closure that takes a reference to a value of type `T` and returns a value of type `U`.
-    ///
-    /// # Returns
-    ///
-    /// Returns a new `Corners<U>` with each field mapped by the provided function.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::{Corners, Pixels};
-    /// let corners = Corners {
-    ///     top_left: Pixels(10.0),
-    ///     top_right: Pixels(20.0),
-    ///     bottom_right: Pixels(30.0),
-    ///     bottom_left: Pixels(40.0),
-    /// };
-    /// let corners_in_rems = corners.map(|&px| Rems(px.0 / 16.0));
-    /// assert_eq!(corners_in_rems, Corners {
-    ///     top_left: Rems(0.625),
-    ///     top_right: Rems(1.25),
-    ///     bottom_right: Rems(1.875),
-    ///     bottom_left: Rems(2.5),
-    /// });
-    /// ```
-    pub fn map<U>(&self, f: impl Fn(&T) -> U) -> Corners<U>
-    where
-        U: Clone + Default + Debug,
-    {
-        Corners {
-            top_left: f(&self.top_left),
-            top_right: f(&self.top_right),
-            bottom_right: f(&self.bottom_right),
-            bottom_left: f(&self.bottom_left),
-        }
-    }
-}
-
-impl<T> Mul for Corners<T>
-where
-    T: Mul<Output = T> + Clone + Default + Debug,
-{
-    type Output = Self;
-
-    fn mul(self, rhs: Self) -> Self::Output {
-        Self {
-            top_left: self.top_left.clone() * rhs.top_left,
-            top_right: self.top_right.clone() * rhs.top_right,
-            bottom_right: self.bottom_right.clone() * rhs.bottom_right,
-            bottom_left: self.bottom_left.clone() * rhs.bottom_left,
-        }
-    }
-}
-
-impl<T, S> MulAssign<S> for Corners<T>
-where
-    T: Mul<S, Output = T> + Clone + Default + Debug,
-    S: Clone,
-{
-    fn mul_assign(&mut self, rhs: S) {
-        self.top_left = self.top_left.clone() * rhs.clone();
-        self.top_right = self.top_right.clone() * rhs.clone();
-        self.bottom_right = self.bottom_right.clone() * rhs.clone();
-        self.bottom_left = self.bottom_left.clone() * rhs;
-    }
-}
-
-impl<T> Copy for Corners<T> where T: Copy + Clone + Default + Debug {}
-
-impl From<f32> for Corners<Pixels> {
-    fn from(val: f32) -> Self {
-        Corners {
-            top_left: val.into(),
-            top_right: val.into(),
-            bottom_right: val.into(),
-            bottom_left: val.into(),
-        }
-    }
-}
-
-impl From<Pixels> for Corners<Pixels> {
-    fn from(val: Pixels) -> Self {
-        Corners {
-            top_left: val,
-            top_right: val,
-            bottom_right: val,
-            bottom_left: val,
-        }
-    }
-}
-
-/// Represents a length in pixels, the base unit of measurement in the UI framework.
-///
-/// `Pixels` is a value type that represents an absolute length in pixels, which is used
-/// for specifying sizes, positions, and distances in the UI. It is the fundamental unit
-/// of measurement for all visual elements and layout calculations.
-///
-/// The inner value is an `f32`, allowing for sub-pixel precision which can be useful for
-/// anti-aliasing and animations. However, when applied to actual pixel grids, the value
-/// is typically rounded to the nearest integer.
-///
-/// # Examples
-///
-/// ```
-/// use zed::Pixels;
-///
-/// // Define a length of 10 pixels
-/// let length = Pixels(10.0);
-///
-/// // Define a length and scale it by a factor of 2
-/// let scaled_length = length.scale(2.0);
-/// assert_eq!(scaled_length, Pixels(20.0));
-/// ```
-#[derive(
-    Clone,
-    Copy,
-    Default,
-    Add,
-    AddAssign,
-    Sub,
-    SubAssign,
-    Neg,
-    Div,
-    DivAssign,
-    PartialEq,
-    Serialize,
-    Deserialize,
-)]
-#[repr(transparent)]
-pub struct Pixels(pub f32);
-
-impl std::ops::Div for Pixels {
-    type Output = f32;
-
-    fn div(self, rhs: Self) -> Self::Output {
-        self.0 / rhs.0
-    }
-}
-
-impl std::ops::DivAssign for Pixels {
-    fn div_assign(&mut self, rhs: Self) {
-        *self = Self(self.0 / rhs.0);
-    }
-}
-
-impl std::ops::RemAssign for Pixels {
-    fn rem_assign(&mut self, rhs: Self) {
-        self.0 %= rhs.0;
-    }
-}
-
-impl std::ops::Rem for Pixels {
-    type Output = Self;
-
-    fn rem(self, rhs: Self) -> Self {
-        Self(self.0 % rhs.0)
-    }
-}
-
-impl Mul<f32> for Pixels {
-    type Output = Pixels;
-
-    fn mul(self, other: f32) -> Pixels {
-        Pixels(self.0 * other)
-    }
-}
-
-impl Mul<usize> for Pixels {
-    type Output = Pixels;
-
-    fn mul(self, other: usize) -> Pixels {
-        Pixels(self.0 * other as f32)
-    }
-}
-
-impl Mul<Pixels> for f32 {
-    type Output = Pixels;
-
-    fn mul(self, rhs: Pixels) -> Self::Output {
-        Pixels(self * rhs.0)
-    }
-}
-
-impl MulAssign<f32> for Pixels {
-    fn mul_assign(&mut self, other: f32) {
-        self.0 *= other;
-    }
-}
-
-impl Pixels {
-    /// Represents zero pixels.
-    pub const ZERO: Pixels = Pixels(0.0);
-    /// The maximum value that can be represented by `Pixels`.
-    pub const MAX: Pixels = Pixels(f32::MAX);
-
-    /// Floors the `Pixels` value to the nearest whole number.
-    ///
-    /// # Returns
-    ///
-    /// Returns a new `Pixels` instance with the floored value.
-    pub fn floor(&self) -> Self {
-        Self(self.0.floor())
-    }
-
-    /// Rounds the `Pixels` value to the nearest whole number.
-    ///
-    /// # Returns
-    ///
-    /// Returns a new `Pixels` instance with the rounded value.
-    pub fn round(&self) -> Self {
-        Self(self.0.round())
-    }
-
-    /// Returns the ceiling of the `Pixels` value to the nearest whole number.
-    ///
-    /// # Returns
-    ///
-    /// Returns a new `Pixels` instance with the ceiling value.
-    pub fn ceil(&self) -> Self {
-        Self(self.0.ceil())
-    }
-
-    /// Scales the `Pixels` value by a given factor, producing `ScaledPixels`.
-    ///
-    /// This method is used when adjusting pixel values for display scaling factors,
-    /// such as high DPI (dots per inch) or Retina displays, where the pixel density is higher and
-    /// thus requires scaling to maintain visual consistency and readability.
-    ///
-    /// The resulting `ScaledPixels` represent the scaled value which can be used for rendering
-    /// calculations where display scaling is considered.
-    pub fn scale(&self, factor: f32) -> ScaledPixels {
-        ScaledPixels(self.0 * factor)
-    }
-
-    /// Raises the `Pixels` value to a given power.
-    ///
-    /// # Arguments
-    ///
-    /// * `exponent` - The exponent to raise the `Pixels` value by.
-    ///
-    /// # Returns
-    ///
-    /// Returns a new `Pixels` instance with the value raised to the given exponent.
-    pub fn pow(&self, exponent: f32) -> Self {
-        Self(self.0.powf(exponent))
-    }
-
-    /// Returns the absolute value of the `Pixels`.
-    ///
-    /// # Returns
-    ///
-    /// A new `Pixels` instance with the absolute value of the original `Pixels`.
-    pub fn abs(&self) -> Self {
-        Self(self.0.abs())
-    }
-}
-
-impl Mul<Pixels> for Pixels {
-    type Output = Pixels;
-
-    fn mul(self, rhs: Pixels) -> Self::Output {
-        Pixels(self.0 * rhs.0)
-    }
-}
-
-impl Eq for Pixels {}
-
-impl PartialOrd for Pixels {
-    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
-        self.0.partial_cmp(&other.0)
-    }
-}
-
-impl Ord for Pixels {
-    fn cmp(&self, other: &Self) -> cmp::Ordering {
-        self.partial_cmp(other).unwrap()
-    }
-}
-
-impl std::hash::Hash for Pixels {
-    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
-        self.0.to_bits().hash(state);
-    }
-}
-
-impl From<f64> for Pixels {
-    fn from(pixels: f64) -> Self {
-        Pixels(pixels as f32)
-    }
-}
-
-impl From<f32> for Pixels {
-    fn from(pixels: f32) -> Self {
-        Pixels(pixels)
-    }
-}
-
-impl Debug for Pixels {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{} px", self.0)
-    }
-}
-
-impl From<Pixels> for f32 {
-    fn from(pixels: Pixels) -> Self {
-        pixels.0
-    }
-}
-
-impl From<&Pixels> for f32 {
-    fn from(pixels: &Pixels) -> Self {
-        pixels.0
-    }
-}
-
-impl From<Pixels> for f64 {
-    fn from(pixels: Pixels) -> Self {
-        pixels.0 as f64
-    }
-}
-
-impl From<Pixels> for u32 {
-    fn from(pixels: Pixels) -> Self {
-        pixels.0 as u32
-    }
-}
-
-impl From<u32> for Pixels {
-    fn from(pixels: u32) -> Self {
-        Pixels(pixels as f32)
-    }
-}
-
-impl From<Pixels> for usize {
-    fn from(pixels: Pixels) -> Self {
-        pixels.0 as usize
-    }
-}
-
-impl From<usize> for Pixels {
-    fn from(pixels: usize) -> Self {
-        Pixels(pixels as f32)
-    }
-}
-
-/// Represents physical pixels on the display.
-///
-/// `DevicePixels` is a unit of measurement that refers to the actual pixels on a device's screen.
-/// This type is used when precise pixel manipulation is required, such as rendering graphics or
-/// interfacing with hardware that operates on the pixel level. Unlike logical pixels that may be
-/// affected by the device's scale factor, `DevicePixels` always correspond to real pixels on the
-/// display.
-#[derive(
-    Add, AddAssign, Clone, Copy, Default, Div, Eq, Hash, Ord, PartialEq, PartialOrd, Sub, SubAssign,
-)]
-#[repr(transparent)]
-pub struct DevicePixels(pub(crate) i32);
-
-impl DevicePixels {
-    /// Converts the `DevicePixels` value to the number of bytes needed to represent it in memory.
-    ///
-    /// This function is useful when working with graphical data that needs to be stored in a buffer,
-    /// such as images or framebuffers, where each pixel may be represented by a specific number of bytes.
-    ///
-    /// # Arguments
-    ///
-    /// * `bytes_per_pixel` - The number of bytes used to represent a single pixel.
-    ///
-    /// # Returns
-    ///
-    /// The number of bytes required to represent the `DevicePixels` value in memory.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::DevicePixels;
-    /// let pixels = DevicePixels(10); // 10 device pixels
-    /// let bytes_per_pixel = 4; // Assume each pixel is represented by 4 bytes (e.g., RGBA)
-    /// let total_bytes = pixels.to_bytes(bytes_per_pixel);
-    /// assert_eq!(total_bytes, 40); // 10 pixels * 4 bytes/pixel = 40 bytes
-    /// ```
-    pub fn to_bytes(&self, bytes_per_pixel: u8) -> u32 {
-        self.0 as u32 * bytes_per_pixel as u32
-    }
-}
-
-impl fmt::Debug for DevicePixels {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{} px (device)", self.0)
-    }
-}
-
-impl From<DevicePixels> for i32 {
-    fn from(device_pixels: DevicePixels) -> Self {
-        device_pixels.0
-    }
-}
-
-impl From<i32> for DevicePixels {
-    fn from(device_pixels: i32) -> Self {
-        DevicePixels(device_pixels)
-    }
-}
-
-impl From<u32> for DevicePixels {
-    fn from(device_pixels: u32) -> Self {
-        DevicePixels(device_pixels as i32)
-    }
-}
-
-impl From<DevicePixels> for u32 {
-    fn from(device_pixels: DevicePixels) -> Self {
-        device_pixels.0 as u32
-    }
-}
-
-impl From<DevicePixels> for u64 {
-    fn from(device_pixels: DevicePixels) -> Self {
-        device_pixels.0 as u64
-    }
-}
-
-impl From<u64> for DevicePixels {
-    fn from(device_pixels: u64) -> Self {
-        DevicePixels(device_pixels as i32)
-    }
-}
-
-impl From<DevicePixels> for usize {
-    fn from(device_pixels: DevicePixels) -> Self {
-        device_pixels.0 as usize
-    }
-}
-
-impl From<usize> for DevicePixels {
-    fn from(device_pixels: usize) -> Self {
-        DevicePixels(device_pixels as i32)
-    }
-}
-
-/// Represents scaled pixels that take into account the device's scale factor.
-///
-/// `ScaledPixels` are used to ensure that UI elements appear at the correct size on devices
-/// with different pixel densities. When a device has a higher scale factor (such as Retina displays),
-/// a single logical pixel may correspond to multiple physical pixels. By using `ScaledPixels`,
-/// dimensions and positions can be specified in a way that scales appropriately across different
-/// display resolutions.
-#[derive(Clone, Copy, Default, Add, AddAssign, Sub, SubAssign, Div, PartialEq, PartialOrd)]
-#[repr(transparent)]
-pub struct ScaledPixels(pub(crate) f32);
-
-impl ScaledPixels {
-    /// Floors the `ScaledPixels` value to the nearest whole number.
-    ///
-    /// # Returns
-    ///
-    /// Returns a new `ScaledPixels` instance with the floored value.
-    pub fn floor(&self) -> Self {
-        Self(self.0.floor())
-    }
-
-    /// Rounds the `ScaledPixels` value to the nearest whole number.
-    ///
-    /// # Returns
-    ///
-    /// Returns a new `ScaledPixels` instance with the rounded value.
-    pub fn ceil(&self) -> Self {
-        Self(self.0.ceil())
-    }
-}
-
-impl Eq for ScaledPixels {}
-
-impl Debug for ScaledPixels {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{} px (scaled)", self.0)
-    }
-}
-
-impl From<ScaledPixels> for DevicePixels {
-    fn from(scaled: ScaledPixels) -> Self {
-        DevicePixels(scaled.0.ceil() as i32)
-    }
-}
-
-impl From<DevicePixels> for ScaledPixels {
-    fn from(device: DevicePixels) -> Self {
-        ScaledPixels(device.0 as f32)
-    }
-}
-
-impl From<ScaledPixels> for f64 {
-    fn from(scaled_pixels: ScaledPixels) -> Self {
-        scaled_pixels.0 as f64
-    }
-}
-
-/// Represents pixels in a global coordinate space, which can span across multiple displays.
-///
-/// `GlobalPixels` is used when dealing with a coordinate system that is not limited to a single
-/// display's boundaries. This type is particularly useful in multi-monitor setups where
-/// positioning and measurements need to be consistent and relative to a "global" origin point
-/// rather than being relative to any individual display.
-#[derive(Clone, Copy, Default, Add, AddAssign, Sub, SubAssign, Div, PartialEq, PartialOrd)]
-#[repr(transparent)]
-pub struct GlobalPixels(pub(crate) f32);
-
-impl Debug for GlobalPixels {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{} px (global coordinate space)", self.0)
-    }
-}
-
-impl From<GlobalPixels> for f64 {
-    fn from(global_pixels: GlobalPixels) -> Self {
-        global_pixels.0 as f64
-    }
-}
-
-impl From<f64> for GlobalPixels {
-    fn from(global_pixels: f64) -> Self {
-        GlobalPixels(global_pixels as f32)
-    }
-}
-
-impl sqlez::bindable::StaticColumnCount for GlobalPixels {}
-
-impl sqlez::bindable::Bind for GlobalPixels {
-    fn bind(
-        &self,
-        statement: &sqlez::statement::Statement,
-        start_index: i32,
-    ) -> anyhow::Result<i32> {
-        self.0.bind(statement, start_index)
-    }
-}
-
-/// Represents a length in rems, a unit based on the font-size of the window, which can be assigned with [WindowContext::set_rem_size].
-///
-/// Rems are used for defining lengths that are scalable and consistent across different UI elements.
-/// The value of `1rem` is typically equal to the font-size of the root element (often the `<html>` element in browsers),
-/// making it a flexible unit that adapts to the user's text size preferences. In this framework, `rems` serve a similar
-/// purpose, allowing for scalable and accessible design that can adjust to different display settings or user preferences.
-///
-/// For example, if the root element's font-size is `16px`, then `1rem` equals `16px`. A length of `2rems` would then be `32px`.
-#[derive(Clone, Copy, Default, Add, Sub, Mul, Div, Neg)]
-pub struct Rems(pub f32);
-
-impl Mul<Pixels> for Rems {
-    type Output = Pixels;
-
-    fn mul(self, other: Pixels) -> Pixels {
-        Pixels(self.0 * other.0)
-    }
-}
-
-impl Debug for Rems {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{} rem", self.0)
-    }
-}
-
-/// Represents an absolute length in pixels or rems.
-///
-/// `AbsoluteLength` can be either a fixed number of pixels, which is an absolute measurement not
-/// affected by the current font size, or a number of rems, which is relative to the font size of
-/// the root element. It is used for specifying dimensions that are either independent of or
-/// related to the typographic scale.
-#[derive(Clone, Copy, Debug, Neg)]
-pub enum AbsoluteLength {
-    /// A length in pixels.
-    Pixels(Pixels),
-    /// A length in rems.
-    Rems(Rems),
-}
-
-impl AbsoluteLength {
-    /// Checks if the absolute length is zero.
-    pub fn is_zero(&self) -> bool {
-        match self {
-            AbsoluteLength::Pixels(px) => px.0 == 0.0,
-            AbsoluteLength::Rems(rems) => rems.0 == 0.0,
-        }
-    }
-}
-
-impl From<Pixels> for AbsoluteLength {
-    fn from(pixels: Pixels) -> Self {
-        AbsoluteLength::Pixels(pixels)
-    }
-}
-
-impl From<Rems> for AbsoluteLength {
-    fn from(rems: Rems) -> Self {
-        AbsoluteLength::Rems(rems)
-    }
-}
-
-impl AbsoluteLength {
-    /// Converts an `AbsoluteLength` to `Pixels` based on a given `rem_size`.
-    ///
-    /// # Arguments
-    ///
-    /// * `rem_size` - The size of one rem in pixels.
-    ///
-    /// # Returns
-    ///
-    /// Returns the `AbsoluteLength` as `Pixels`.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::{AbsoluteLength, Pixels};
-    /// let length_in_pixels = AbsoluteLength::Pixels(Pixels(42.0));
-    /// let length_in_rems = AbsoluteLength::Rems(Rems(2.0));
-    /// let rem_size = Pixels(16.0);
-    ///
-    /// assert_eq!(length_in_pixels.to_pixels(rem_size), Pixels(42.0));
-    /// assert_eq!(length_in_rems.to_pixels(rem_size), Pixels(32.0));
-    /// ```
-    pub fn to_pixels(&self, rem_size: Pixels) -> Pixels {
-        match self {
-            AbsoluteLength::Pixels(pixels) => *pixels,
-            AbsoluteLength::Rems(rems) => *rems * rem_size,
-        }
-    }
-}
-
-impl Default for AbsoluteLength {
-    fn default() -> Self {
-        px(0.).into()
-    }
-}
-
-/// A non-auto length that can be defined in pixels, rems, or percent of parent.
-///
-/// This enum represents lengths that have a specific value, as opposed to lengths that are automatically
-/// determined by the context. It includes absolute lengths in pixels or rems, and relative lengths as a
-/// fraction of the parent's size.
-#[derive(Clone, Copy, Neg)]
-pub enum DefiniteLength {
-    /// An absolute length specified in pixels or rems.
-    Absolute(AbsoluteLength),
-    /// A relative length specified as a fraction of the parent's size, between 0 and 1.
-    Fraction(f32),
-}
-
-impl DefiniteLength {
-    /// Converts the `DefiniteLength` to `Pixels` based on a given `base_size` and `rem_size`.
-    ///
-    /// If the `DefiniteLength` is an absolute length, it will be directly converted to `Pixels`.
-    /// If it is a fraction, the fraction will be multiplied by the `base_size` to get the length in pixels.
-    ///
-    /// # Arguments
-    ///
-    /// * `base_size` - The base size in `AbsoluteLength` to which the fraction will be applied.
-    /// * `rem_size` - The size of one rem in pixels, used to convert rems to pixels.
-    ///
-    /// # Returns
-    ///
-    /// Returns the `DefiniteLength` as `Pixels`.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use zed::{DefiniteLength, AbsoluteLength, Pixels, px, rems};
-    /// let length_in_pixels = DefiniteLength::Absolute(AbsoluteLength::Pixels(px(42.0)));
-    /// let length_in_rems = DefiniteLength::Absolute(AbsoluteLength::Rems(rems(2.0)));
-    /// let length_as_fraction = DefiniteLength::Fraction(0.5);
-    /// let base_size = AbsoluteLength::Pixels(px(100.0));
-    /// let rem_size = px(16.0);
-    ///
-    /// assert_eq!(length_in_pixels.to_pixels(base_size, rem_size), Pixels(42.0));
-    /// assert_eq!(length_in_rems.to_pixels(base_size, rem_size), Pixels(32.0));
-    /// assert_eq!(length_as_fraction.to_pixels(base_size, rem_size), Pixels(50.0));
-    /// ```
-    pub fn to_pixels(&self, base_size: AbsoluteLength, rem_size: Pixels) -> Pixels {
-        match self {
-            DefiniteLength::Absolute(size) => size.to_pixels(rem_size),
-            DefiniteLength::Fraction(fraction) => match base_size {
-                AbsoluteLength::Pixels(px) => px * *fraction,
-                AbsoluteLength::Rems(rems) => rems * rem_size * *fraction,
-            },
-        }
-    }
-}
-
-impl Debug for DefiniteLength {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            DefiniteLength::Absolute(length) => Debug::fmt(length, f),
-            DefiniteLength::Fraction(fract) => write!(f, "{}%", (fract * 100.0) as i32),
-        }
-    }
-}
-
-impl From<Pixels> for DefiniteLength {
-    fn from(pixels: Pixels) -> Self {
-        Self::Absolute(pixels.into())
-    }
-}
-
-impl From<Rems> for DefiniteLength {
-    fn from(rems: Rems) -> Self {
-        Self::Absolute(rems.into())
-    }
-}
-
-impl From<AbsoluteLength> for DefiniteLength {
-    fn from(length: AbsoluteLength) -> Self {
-        Self::Absolute(length)
-    }
-}
-
-impl Default for DefiniteLength {
-    fn default() -> Self {
-        Self::Absolute(AbsoluteLength::default())
-    }
-}
-
-/// A length that can be defined in pixels, rems, percent of parent, or auto.
-#[derive(Clone, Copy)]
-pub enum Length {
-    /// A definite length specified either in pixels, rems, or as a fraction of the parent's size.
-    Definite(DefiniteLength),
-    /// An automatic length that is determined by the context in which it is used.
-    Auto,
-}
-
-impl Debug for Length {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Length::Definite(definite_length) => write!(f, "{:?}", definite_length),
-            Length::Auto => write!(f, "auto"),
-        }
-    }
-}
-
-/// Constructs a `DefiniteLength` representing a relative fraction of a parent size.
-///
-/// This function creates a `DefiniteLength` that is a specified fraction of a parent's dimension.
-/// The fraction should be a floating-point number between 0.0 and 1.0, where 1.0 represents 100% of the parent's size.
-///
-/// # Arguments
-///
-/// * `fraction` - The fraction of the parent's size, between 0.0 and 1.0.
-///
-/// # Returns
-///
-/// A `DefiniteLength` representing the relative length as a fraction of the parent's size.
-pub fn relative(fraction: f32) -> DefiniteLength {
-    DefiniteLength::Fraction(fraction)
-}
-
-/// Returns the Golden Ratio, i.e. `~(1.0 + sqrt(5.0)) / 2.0`.
-pub fn phi() -> DefiniteLength {
-    relative(1.618_034)
-}
-
-/// Constructs a `Rems` value representing a length in rems.
-///
-/// # Arguments
-///
-/// * `rems` - The number of rems for the length.
-///
-/// # Returns
-///
-/// A `Rems` representing the specified number of rems.
-pub fn rems(rems: f32) -> Rems {
-    Rems(rems)
-}
-
-/// Constructs a `Pixels` value representing a length in pixels.
-///
-/// # Arguments
-///
-/// * `pixels` - The number of pixels for the length.
-///
-/// # Returns
-///
-/// A `Pixels` representing the specified number of pixels.
-pub const fn px(pixels: f32) -> Pixels {
-    Pixels(pixels)
-}
-
-/// Returns a `Length` representing an automatic length.
-///
-/// The `auto` length is often used in layout calculations where the length should be determined
-/// by the layout context itself rather than being explicitly set. This is commonly used in CSS
-/// for properties like `width`, `height`, `margin`, `padding`, etc., where `auto` can be used
-/// to instruct the layout engine to calculate the size based on other factors like the size of the
-/// container or the intrinsic size of the content.
-///
-/// # Returns
-///
-/// A `Length` variant set to `Auto`.
-pub fn auto() -> Length {
-    Length::Auto
-}
-
-impl From<Pixels> for Length {
-    fn from(pixels: Pixels) -> Self {
-        Self::Definite(pixels.into())
-    }
-}
-
-impl From<Rems> for Length {
-    fn from(rems: Rems) -> Self {
-        Self::Definite(rems.into())
-    }
-}
-
-impl From<DefiniteLength> for Length {
-    fn from(length: DefiniteLength) -> Self {
-        Self::Definite(length)
-    }
-}
-
-impl From<AbsoluteLength> for Length {
-    fn from(length: AbsoluteLength) -> Self {
-        Self::Definite(length.into())
-    }
-}
-
-impl Default for Length {
-    fn default() -> Self {
-        Self::Definite(DefiniteLength::default())
-    }
-}
-
-impl From<()> for Length {
-    fn from(_: ()) -> Self {
-        Self::Definite(DefiniteLength::default())
-    }
-}
-
-/// Provides a trait for types that can calculate half of their value.
-///
-/// The `Half` trait is used for types that can be evenly divided, returning a new instance of the same type
-/// representing half of the original value. This is commonly used for types that represent measurements or sizes,
-/// such as lengths or pixels, where halving is a frequent operation during layout calculations or animations.
-pub trait Half {
-    /// Returns half of the current value.
-    ///
-    /// # Returns
-    ///
-    /// A new instance of the implementing type, representing half of the original value.
-    fn half(&self) -> Self;
-}
-
-impl Half for f32 {
-    fn half(&self) -> Self {
-        self / 2.
-    }
-}
-
-impl Half for DevicePixels {
-    fn half(&self) -> Self {
-        Self(self.0 / 2)
-    }
-}
-
-impl Half for ScaledPixels {
-    fn half(&self) -> Self {
-        Self(self.0 / 2.)
-    }
-}
-
-impl Half for Pixels {
-    fn half(&self) -> Self {
-        Self(self.0 / 2.)
-    }
-}
-
-impl Half for Rems {
-    fn half(&self) -> Self {
-        Self(self.0 / 2.)
-    }
-}
-
-impl Half for GlobalPixels {
-    fn half(&self) -> Self {
-        Self(self.0 / 2.)
-    }
-}
-
-/// A trait for checking if a value is zero.
-///
-/// This trait provides a method to determine if a value is considered to be zero.
-/// It is implemented for various numeric and length-related types where the concept
-/// of zero is applicable. This can be useful for comparisons, optimizations, or
-/// determining if an operation has a neutral effect.
-pub trait IsZero {
-    /// Determines if the value is zero.
-    ///
-    /// # Returns
-    ///
-    /// Returns `true` if the value is zero, `false` otherwise.
-    fn is_zero(&self) -> bool;
-}
-
-impl IsZero for DevicePixels {
-    fn is_zero(&self) -> bool {
-        self.0 == 0
-    }
-}
-
-impl IsZero for ScaledPixels {
-    fn is_zero(&self) -> bool {
-        self.0 == 0.
-    }
-}
-
-impl IsZero for Pixels {
-    fn is_zero(&self) -> bool {
-        self.0 == 0.
-    }
-}
-
-impl IsZero for Rems {
-    fn is_zero(&self) -> bool {
-        self.0 == 0.
-    }
-}
-
-impl IsZero for AbsoluteLength {
-    fn is_zero(&self) -> bool {
-        match self {
-            AbsoluteLength::Pixels(pixels) => pixels.is_zero(),
-            AbsoluteLength::Rems(rems) => rems.is_zero(),
-        }
-    }
-}
-
-impl IsZero for DefiniteLength {
-    fn is_zero(&self) -> bool {
-        match self {
-            DefiniteLength::Absolute(length) => length.is_zero(),
-            DefiniteLength::Fraction(fraction) => *fraction == 0.,
-        }
-    }
-}
-
-impl IsZero for Length {
-    fn is_zero(&self) -> bool {
-        match self {
-            Length::Definite(length) => length.is_zero(),
-            Length::Auto => false,
-        }
-    }
-}
-
-impl<T: IsZero + Debug + Clone + Default> IsZero for Point<T> {
-    fn is_zero(&self) -> bool {
-        self.x.is_zero() && self.y.is_zero()
-    }
-}
-
-impl<T> IsZero for Size<T>
-where
-    T: IsZero + Default + Debug + Clone,
-{
-    fn is_zero(&self) -> bool {
-        self.width.is_zero() || self.height.is_zero()
-    }
-}
-
-impl<T: IsZero + Debug + Clone + Default> IsZero for Bounds<T> {
-    fn is_zero(&self) -> bool {
-        self.size.is_zero()
-    }
-}
-
-impl<T> IsZero for Corners<T>
-where
-    T: IsZero + Clone + Default + Debug,
-{
-    fn is_zero(&self) -> bool {
-        self.top_left.is_zero()
-            && self.top_right.is_zero()
-            && self.bottom_right.is_zero()
-            && self.bottom_left.is_zero()
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn test_bounds_intersects() {
-        let bounds1 = Bounds {
-            origin: Point { x: 0.0, y: 0.0 },
-            size: Size {
-                width: 5.0,
-                height: 5.0,
-            },
-        };
-        let bounds2 = Bounds {
-            origin: Point { x: 4.0, y: 4.0 },
-            size: Size {
-                width: 5.0,
-                height: 5.0,
-            },
-        };
-        let bounds3 = Bounds {
-            origin: Point { x: 10.0, y: 10.0 },
-            size: Size {
-                width: 5.0,
-                height: 5.0,
-            },
-        };
-
-        // Test Case 1: Intersecting bounds
-        assert_eq!(bounds1.intersects(&bounds2), true);
-
-        // Test Case 2: Non-Intersecting bounds
-        assert_eq!(bounds1.intersects(&bounds3), false);
-
-        // Test Case 3: Bounds intersecting with themselves
-        assert_eq!(bounds1.intersects(&bounds1), true);
-    }
-}

crates/gpui2/src/gpui2.rs 🔗

@@ -1,215 +0,0 @@
-#[macro_use]
-mod action;
-mod app;
-
-mod arena;
-mod assets;
-mod color;
-mod element;
-mod elements;
-mod executor;
-mod geometry;
-mod image_cache;
-mod input;
-mod interactive;
-mod key_dispatch;
-mod keymap;
-mod platform;
-pub mod prelude;
-mod scene;
-mod shared_string;
-mod style;
-mod styled;
-mod subscription;
-mod svg_renderer;
-mod taffy;
-#[cfg(any(test, feature = "test-support"))]
-pub mod test;
-mod text_system;
-mod util;
-mod view;
-mod window;
-
-mod private {
-    /// A mechanism for restricting implementations of a trait to only those in GPUI.
-    /// See: https://predr.ag/blog/definitive-guide-to-sealed-traits-in-rust/
-    pub trait Sealed {}
-}
-
-pub use action::*;
-pub use anyhow::Result;
-pub use app::*;
-pub(crate) use arena::*;
-pub use assets::*;
-pub use color::*;
-pub use ctor::ctor;
-pub use element::*;
-pub use elements::*;
-pub use executor::*;
-pub use geometry::*;
-pub use gpui2_macros::*;
-pub use image_cache::*;
-pub use input::*;
-pub use interactive::*;
-pub use key_dispatch::*;
-pub use keymap::*;
-pub use linkme;
-pub use platform::*;
-use private::Sealed;
-pub use refineable::*;
-pub use scene::*;
-pub use serde;
-pub use serde_derive;
-pub use serde_json;
-pub use shared_string::*;
-pub use smallvec;
-pub use smol::Timer;
-pub use style::*;
-pub use styled::*;
-pub use subscription::*;
-pub use svg_renderer::*;
-pub use taffy::{AvailableSpace, LayoutId};
-#[cfg(any(test, feature = "test-support"))]
-pub use test::*;
-pub use text_system::*;
-pub use util::arc_cow::ArcCow;
-pub use view::*;
-pub use window::*;
-
-use std::{
-    any::{Any, TypeId},
-    borrow::BorrowMut,
-};
-use taffy::TaffyLayoutEngine;
-
-pub trait Context {
-    type Result<T>;
-
-    fn new_model<T: 'static>(
-        &mut self,
-        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
-    ) -> Self::Result<Model<T>>;
-
-    fn update_model<T, R>(
-        &mut self,
-        handle: &Model<T>,
-        update: impl FnOnce(&mut T, &mut ModelContext<'_, T>) -> R,
-    ) -> Self::Result<R>
-    where
-        T: 'static;
-
-    fn read_model<T, R>(
-        &self,
-        handle: &Model<T>,
-        read: impl FnOnce(&T, &AppContext) -> R,
-    ) -> Self::Result<R>
-    where
-        T: 'static;
-
-    fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
-    where
-        F: FnOnce(AnyView, &mut WindowContext<'_>) -> T;
-
-    fn read_window<T, R>(
-        &self,
-        window: &WindowHandle<T>,
-        read: impl FnOnce(View<T>, &AppContext) -> R,
-    ) -> Result<R>
-    where
-        T: 'static;
-}
-
-pub trait VisualContext: Context {
-    fn new_view<V>(
-        &mut self,
-        build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
-    ) -> Self::Result<View<V>>
-    where
-        V: 'static + Render;
-
-    fn update_view<V: 'static, R>(
-        &mut self,
-        view: &View<V>,
-        update: impl FnOnce(&mut V, &mut ViewContext<'_, V>) -> R,
-    ) -> Self::Result<R>;
-
-    fn replace_root_view<V>(
-        &mut self,
-        build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
-    ) -> Self::Result<View<V>>
-    where
-        V: 'static + Render;
-
-    fn focus_view<V>(&mut self, view: &View<V>) -> Self::Result<()>
-    where
-        V: FocusableView;
-
-    fn dismiss_view<V>(&mut self, view: &View<V>) -> Self::Result<()>
-    where
-        V: ManagedView;
-}
-
-pub trait Entity<T>: Sealed {
-    type Weak: 'static;
-
-    fn entity_id(&self) -> EntityId;
-    fn downgrade(&self) -> Self::Weak;
-    fn upgrade_from(weak: &Self::Weak) -> Option<Self>
-    where
-        Self: Sized;
-}
-
-pub trait EventEmitter<E: Any>: 'static {}
-
-pub enum GlobalKey {
-    Numeric(usize),
-    View(EntityId),
-    Type(TypeId),
-}
-
-pub trait BorrowAppContext {
-    fn with_text_style<F, R>(&mut self, style: Option<TextStyleRefinement>, f: F) -> R
-    where
-        F: FnOnce(&mut Self) -> R;
-
-    fn set_global<T: 'static>(&mut self, global: T);
-}
-
-impl<C> BorrowAppContext for C
-where
-    C: BorrowMut<AppContext>,
-{
-    fn with_text_style<F, R>(&mut self, style: Option<TextStyleRefinement>, f: F) -> R
-    where
-        F: FnOnce(&mut Self) -> R,
-    {
-        if let Some(style) = style {
-            self.borrow_mut().push_text_style(style);
-            let result = f(self);
-            self.borrow_mut().pop_text_style();
-            result
-        } else {
-            f(self)
-        }
-    }
-
-    fn set_global<G: 'static>(&mut self, global: G) {
-        self.borrow_mut().set_global(global)
-    }
-}
-
-pub trait Flatten<T> {
-    fn flatten(self) -> Result<T>;
-}
-
-impl<T> Flatten<T> for Result<Result<T>> {
-    fn flatten(self) -> Result<T> {
-        self?
-    }
-}
-
-impl<T> Flatten<T> for Result<T> {
-    fn flatten(self) -> Result<T> {
-        self
-    }
-}

crates/gpui2/src/image_cache.rs 🔗

@@ -1,107 +0,0 @@
-use crate::{ImageData, ImageId, SharedString};
-use collections::HashMap;
-use futures::{
-    future::{BoxFuture, Shared},
-    AsyncReadExt, FutureExt, TryFutureExt,
-};
-use image::ImageError;
-use parking_lot::Mutex;
-use std::sync::Arc;
-use thiserror::Error;
-use util::http::{self, HttpClient};
-
-#[derive(PartialEq, Eq, Hash, Clone)]
-pub struct RenderImageParams {
-    pub(crate) image_id: ImageId,
-}
-
-#[derive(Debug, Error, Clone)]
-pub enum Error {
-    #[error("http error: {0}")]
-    Client(#[from] http::Error),
-    #[error("IO error: {0}")]
-    Io(Arc<std::io::Error>),
-    #[error("unexpected http status: {status}, body: {body}")]
-    BadStatus {
-        status: http::StatusCode,
-        body: String,
-    },
-    #[error("image error: {0}")]
-    Image(Arc<ImageError>),
-}
-
-impl From<std::io::Error> for Error {
-    fn from(error: std::io::Error) -> Self {
-        Error::Io(Arc::new(error))
-    }
-}
-
-impl From<ImageError> for Error {
-    fn from(error: ImageError) -> Self {
-        Error::Image(Arc::new(error))
-    }
-}
-
-pub struct ImageCache {
-    client: Arc<dyn HttpClient>,
-    images: Arc<Mutex<HashMap<SharedString, FetchImageFuture>>>,
-}
-
-type FetchImageFuture = Shared<BoxFuture<'static, Result<Arc<ImageData>, Error>>>;
-
-impl ImageCache {
-    pub fn new(client: Arc<dyn HttpClient>) -> Self {
-        ImageCache {
-            client,
-            images: Default::default(),
-        }
-    }
-
-    pub fn get(
-        &self,
-        uri: impl Into<SharedString>,
-    ) -> Shared<BoxFuture<'static, Result<Arc<ImageData>, Error>>> {
-        let uri = uri.into();
-        let mut images = self.images.lock();
-
-        match images.get(&uri) {
-            Some(future) => future.clone(),
-            None => {
-                let client = self.client.clone();
-                let future = {
-                    let uri = uri.clone();
-                    async move {
-                        let mut response = client.get(uri.as_ref(), ().into(), true).await?;
-                        let mut body = Vec::new();
-                        response.body_mut().read_to_end(&mut body).await?;
-
-                        if !response.status().is_success() {
-                            return Err(Error::BadStatus {
-                                status: response.status(),
-                                body: String::from_utf8_lossy(&body).into_owned(),
-                            });
-                        }
-
-                        let format = image::guess_format(&body)?;
-                        let image =
-                            image::load_from_memory_with_format(&body, format)?.into_bgra8();
-                        Ok(Arc::new(ImageData::new(image)))
-                    }
-                }
-                .map_err({
-                    let uri = uri.clone();
-
-                    move |error| {
-                        log::log!(log::Level::Error, "{:?} {:?}", &uri, &error);
-                        error
-                    }
-                })
-                .boxed()
-                .shared();
-
-                images.insert(uri, future.clone());
-                future
-            }
-        }
-    }
-}

crates/gpui2/src/platform.rs 🔗

@@ -1,592 +0,0 @@
-mod app_menu;
-mod keystroke;
-#[cfg(target_os = "macos")]
-mod mac;
-#[cfg(any(test, feature = "test-support"))]
-mod test;
-
-use crate::{
-    point, size, Action, AnyWindowHandle, BackgroundExecutor, Bounds, DevicePixels, Font, FontId,
-    FontMetrics, FontRun, ForegroundExecutor, GlobalPixels, GlyphId, InputEvent, Keymap,
-    LineLayout, Pixels, Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result,
-    Scene, SharedString, Size, TaskLabel,
-};
-use anyhow::{anyhow, bail};
-use async_task::Runnable;
-use futures::channel::oneshot;
-use parking::Unparker;
-use seahash::SeaHasher;
-use serde::{Deserialize, Serialize};
-use sqlez::bindable::{Bind, Column, StaticColumnCount};
-use sqlez::statement::Statement;
-use std::borrow::Cow;
-use std::hash::{Hash, Hasher};
-use std::time::Duration;
-use std::{
-    any::Any,
-    fmt::{self, Debug, Display},
-    ops::Range,
-    path::{Path, PathBuf},
-    rc::Rc,
-    str::FromStr,
-    sync::Arc,
-};
-use uuid::Uuid;
-
-pub use app_menu::*;
-pub use keystroke::*;
-#[cfg(target_os = "macos")]
-pub use mac::*;
-#[cfg(any(test, feature = "test-support"))]
-pub use test::*;
-pub use time::UtcOffset;
-
-#[cfg(target_os = "macos")]
-pub(crate) fn current_platform() -> Rc<dyn Platform> {
-    Rc::new(MacPlatform::new())
-}
-
-pub type DrawWindow = Box<dyn FnMut() -> Result<Scene>>;
-
-pub(crate) trait Platform: 'static {
-    fn background_executor(&self) -> BackgroundExecutor;
-    fn foreground_executor(&self) -> ForegroundExecutor;
-    fn text_system(&self) -> Arc<dyn PlatformTextSystem>;
-
-    fn run(&self, on_finish_launching: Box<dyn 'static + FnOnce()>);
-    fn quit(&self);
-    fn restart(&self);
-    fn activate(&self, ignoring_other_apps: bool);
-    fn hide(&self);
-    fn hide_other_apps(&self);
-    fn unhide_other_apps(&self);
-
-    fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>>;
-    fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>>;
-    fn active_window(&self) -> Option<AnyWindowHandle>;
-    fn open_window(
-        &self,
-        handle: AnyWindowHandle,
-        options: WindowOptions,
-        draw: DrawWindow,
-    ) -> Box<dyn PlatformWindow>;
-
-    fn set_display_link_output_callback(
-        &self,
-        display_id: DisplayId,
-        callback: Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp) + Send>,
-    );
-    fn start_display_link(&self, display_id: DisplayId);
-    fn stop_display_link(&self, display_id: DisplayId);
-    // fn add_status_item(&self, _handle: AnyWindowHandle) -> Box<dyn PlatformWindow>;
-
-    fn open_url(&self, url: &str);
-    fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>);
-    fn prompt_for_paths(
-        &self,
-        options: PathPromptOptions,
-    ) -> oneshot::Receiver<Option<Vec<PathBuf>>>;
-    fn prompt_for_new_path(&self, directory: &Path) -> oneshot::Receiver<Option<PathBuf>>;
-    fn reveal_path(&self, path: &Path);
-
-    fn on_become_active(&self, callback: Box<dyn FnMut()>);
-    fn on_resign_active(&self, callback: Box<dyn FnMut()>);
-    fn on_quit(&self, callback: Box<dyn FnMut()>);
-    fn on_reopen(&self, callback: Box<dyn FnMut()>);
-    fn on_event(&self, callback: Box<dyn FnMut(InputEvent) -> bool>);
-
-    fn set_menus(&self, menus: Vec<Menu>, keymap: &Keymap);
-    fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>);
-    fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>);
-    fn on_validate_app_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>);
-
-    fn os_name(&self) -> &'static str;
-    fn os_version(&self) -> Result<SemanticVersion>;
-    fn app_version(&self) -> Result<SemanticVersion>;
-    fn app_path(&self) -> Result<PathBuf>;
-    fn local_timezone(&self) -> UtcOffset;
-    fn double_click_interval(&self) -> Duration;
-    fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf>;
-
-    fn set_cursor_style(&self, style: CursorStyle);
-    fn should_auto_hide_scrollbars(&self) -> bool;
-
-    fn write_to_clipboard(&self, item: ClipboardItem);
-    fn read_from_clipboard(&self) -> Option<ClipboardItem>;
-
-    fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Result<()>;
-    fn read_credentials(&self, url: &str) -> Result<Option<(String, Vec<u8>)>>;
-    fn delete_credentials(&self, url: &str) -> Result<()>;
-}
-
-pub trait PlatformDisplay: Send + Sync + Debug {
-    fn id(&self) -> DisplayId;
-    /// Returns a stable identifier for this display that can be persisted and used
-    /// across system restarts.
-    fn uuid(&self) -> Result<Uuid>;
-    fn as_any(&self) -> &dyn Any;
-    fn bounds(&self) -> Bounds<GlobalPixels>;
-}
-
-#[derive(PartialEq, Eq, Hash, Copy, Clone)]
-pub struct DisplayId(pub(crate) u32);
-
-impl Debug for DisplayId {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "DisplayId({})", self.0)
-    }
-}
-
-unsafe impl Send for DisplayId {}
-
-pub trait PlatformWindow {
-    fn bounds(&self) -> WindowBounds;
-    fn content_size(&self) -> Size<Pixels>;
-    fn scale_factor(&self) -> f32;
-    fn titlebar_height(&self) -> Pixels;
-    fn appearance(&self) -> WindowAppearance;
-    fn display(&self) -> Rc<dyn PlatformDisplay>;
-    fn mouse_position(&self) -> Point<Pixels>;
-    fn modifiers(&self) -> Modifiers;
-    fn as_any_mut(&mut self) -> &mut dyn Any;
-    fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>);
-    fn clear_input_handler(&mut self);
-    fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver<usize>;
-    fn activate(&self);
-    fn set_title(&mut self, title: &str);
-    fn set_edited(&mut self, edited: bool);
-    fn show_character_palette(&self);
-    fn minimize(&self);
-    fn zoom(&self);
-    fn toggle_full_screen(&self);
-    fn on_input(&self, callback: Box<dyn FnMut(InputEvent) -> bool>);
-    fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>);
-    fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>);
-    fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>);
-    fn on_moved(&self, callback: Box<dyn FnMut()>);
-    fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>);
-    fn on_close(&self, callback: Box<dyn FnOnce()>);
-    fn on_appearance_changed(&self, callback: Box<dyn FnMut()>);
-    fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool;
-    fn invalidate(&self);
-
-    fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas>;
-
-    #[cfg(any(test, feature = "test-support"))]
-    fn as_test(&mut self) -> Option<&mut TestWindow> {
-        None
-    }
-}
-
-pub trait PlatformDispatcher: Send + Sync {
-    fn is_main_thread(&self) -> bool;
-    fn dispatch(&self, runnable: Runnable, label: Option<TaskLabel>);
-    fn dispatch_on_main_thread(&self, runnable: Runnable);
-    fn dispatch_after(&self, duration: Duration, runnable: Runnable);
-    fn tick(&self, background_only: bool) -> bool;
-    fn park(&self);
-    fn unparker(&self) -> Unparker;
-
-    #[cfg(any(test, feature = "test-support"))]
-    fn as_test(&self) -> Option<&TestDispatcher> {
-        None
-    }
-}
-
-pub trait PlatformTextSystem: Send + Sync {
-    fn add_fonts(&self, fonts: &[Arc<Vec<u8>>]) -> Result<()>;
-    fn all_font_families(&self) -> Vec<String>;
-    fn font_id(&self, descriptor: &Font) -> Result<FontId>;
-    fn font_metrics(&self, font_id: FontId) -> FontMetrics;
-    fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>>;
-    fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Size<f32>>;
-    fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId>;
-    fn glyph_raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>>;
-    fn rasterize_glyph(
-        &self,
-        params: &RenderGlyphParams,
-        raster_bounds: Bounds<DevicePixels>,
-    ) -> Result<(Size<DevicePixels>, Vec<u8>)>;
-    fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> LineLayout;
-    fn wrap_line(
-        &self,
-        text: &str,
-        font_id: FontId,
-        font_size: Pixels,
-        width: Pixels,
-    ) -> Vec<usize>;
-}
-
-#[derive(Clone, Debug)]
-pub struct AppMetadata {
-    pub os_name: &'static str,
-    pub os_version: Option<SemanticVersion>,
-    pub app_version: Option<SemanticVersion>,
-}
-
-#[derive(PartialEq, Eq, Hash, Clone)]
-pub enum AtlasKey {
-    Glyph(RenderGlyphParams),
-    Svg(RenderSvgParams),
-    Image(RenderImageParams),
-}
-
-impl AtlasKey {
-    pub(crate) fn texture_kind(&self) -> AtlasTextureKind {
-        match self {
-            AtlasKey::Glyph(params) => {
-                if params.is_emoji {
-                    AtlasTextureKind::Polychrome
-                } else {
-                    AtlasTextureKind::Monochrome
-                }
-            }
-            AtlasKey::Svg(_) => AtlasTextureKind::Monochrome,
-            AtlasKey::Image(_) => AtlasTextureKind::Polychrome,
-        }
-    }
-}
-
-impl From<RenderGlyphParams> for AtlasKey {
-    fn from(params: RenderGlyphParams) -> Self {
-        Self::Glyph(params)
-    }
-}
-
-impl From<RenderSvgParams> for AtlasKey {
-    fn from(params: RenderSvgParams) -> Self {
-        Self::Svg(params)
-    }
-}
-
-impl From<RenderImageParams> for AtlasKey {
-    fn from(params: RenderImageParams) -> Self {
-        Self::Image(params)
-    }
-}
-
-pub trait PlatformAtlas: Send + Sync {
-    fn get_or_insert_with<'a>(
-        &self,
-        key: &AtlasKey,
-        build: &mut dyn FnMut() -> Result<(Size<DevicePixels>, Cow<'a, [u8]>)>,
-    ) -> Result<AtlasTile>;
-
-    fn clear(&self);
-}
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-#[repr(C)]
-pub struct AtlasTile {
-    pub(crate) texture_id: AtlasTextureId,
-    pub(crate) tile_id: TileId,
-    pub(crate) bounds: Bounds<DevicePixels>,
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-#[repr(C)]
-pub(crate) struct AtlasTextureId {
-    // We use u32 instead of usize for Metal Shader Language compatibility
-    pub(crate) index: u32,
-    pub(crate) kind: AtlasTextureKind,
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-#[repr(C)]
-pub(crate) enum AtlasTextureKind {
-    Monochrome = 0,
-    Polychrome = 1,
-    Path = 2,
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
-#[repr(C)]
-pub(crate) struct TileId(pub(crate) u32);
-
-impl From<etagere::AllocId> for TileId {
-    fn from(id: etagere::AllocId) -> Self {
-        Self(id.serialize())
-    }
-}
-
-impl From<TileId> for etagere::AllocId {
-    fn from(id: TileId) -> Self {
-        Self::deserialize(id.0)
-    }
-}
-
-pub trait PlatformInputHandler: 'static {
-    fn selected_text_range(&mut self) -> Option<Range<usize>>;
-    fn marked_text_range(&mut self) -> Option<Range<usize>>;
-    fn text_for_range(&mut self, range_utf16: Range<usize>) -> Option<String>;
-    fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str);
-    fn replace_and_mark_text_in_range(
-        &mut self,
-        range_utf16: Option<Range<usize>>,
-        new_text: &str,
-        new_selected_range: Option<Range<usize>>,
-    );
-    fn unmark_text(&mut self);
-    fn bounds_for_range(&mut self, range_utf16: Range<usize>) -> Option<Bounds<Pixels>>;
-}
-
-#[derive(Debug)]
-pub struct WindowOptions {
-    pub bounds: WindowBounds,
-    pub titlebar: Option<TitlebarOptions>,
-    pub center: bool,
-    pub focus: bool,
-    pub show: bool,
-    pub kind: WindowKind,
-    pub is_movable: bool,
-    pub display_id: Option<DisplayId>,
-}
-
-impl Default for WindowOptions {
-    fn default() -> Self {
-        Self {
-            bounds: WindowBounds::default(),
-            titlebar: Some(TitlebarOptions {
-                title: Default::default(),
-                appears_transparent: Default::default(),
-                traffic_light_position: Default::default(),
-            }),
-            center: false,
-            focus: true,
-            show: true,
-            kind: WindowKind::Normal,
-            is_movable: true,
-            display_id: None,
-        }
-    }
-}
-
-#[derive(Debug, Default)]
-pub struct TitlebarOptions {
-    pub title: Option<SharedString>,
-    pub appears_transparent: bool,
-    pub traffic_light_position: Option<Point<Pixels>>,
-}
-
-#[derive(Copy, Clone, Debug)]
-pub enum Appearance {
-    Light,
-    VibrantLight,
-    Dark,
-    VibrantDark,
-}
-
-impl Default for Appearance {
-    fn default() -> Self {
-        Self::Light
-    }
-}
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum WindowKind {
-    Normal,
-    PopUp,
-}
-
-#[derive(Copy, Clone, Debug, PartialEq, Default)]
-pub enum WindowBounds {
-    Fullscreen,
-    #[default]
-    Maximized,
-    Fixed(Bounds<GlobalPixels>),
-}
-
-impl StaticColumnCount for WindowBounds {
-    fn column_count() -> usize {
-        5
-    }
-}
-
-impl Bind for WindowBounds {
-    fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
-        let (region, next_index) = match self {
-            WindowBounds::Fullscreen => {
-                let next_index = statement.bind(&"Fullscreen", start_index)?;
-                (None, next_index)
-            }
-            WindowBounds::Maximized => {
-                let next_index = statement.bind(&"Maximized", start_index)?;
-                (None, next_index)
-            }
-            WindowBounds::Fixed(region) => {
-                let next_index = statement.bind(&"Fixed", start_index)?;
-                (Some(*region), next_index)
-            }
-        };
-
-        statement.bind(
-            &region.map(|region| {
-                (
-                    region.origin.x,
-                    region.origin.y,
-                    region.size.width,
-                    region.size.height,
-                )
-            }),
-            next_index,
-        )
-    }
-}
-
-impl Column for WindowBounds {
-    fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
-        let (window_state, next_index) = String::column(statement, start_index)?;
-        let bounds = match window_state.as_str() {
-            "Fullscreen" => WindowBounds::Fullscreen,
-            "Maximized" => WindowBounds::Maximized,
-            "Fixed" => {
-                let ((x, y, width, height), _) = Column::column(statement, next_index)?;
-                let x: f64 = x;
-                let y: f64 = y;
-                let width: f64 = width;
-                let height: f64 = height;
-                WindowBounds::Fixed(Bounds {
-                    origin: point(x.into(), y.into()),
-                    size: size(width.into(), height.into()),
-                })
-            }
-            _ => bail!("Window State did not have a valid string"),
-        };
-
-        Ok((bounds, next_index + 4))
-    }
-}
-
-#[derive(Copy, Clone, Debug)]
-pub enum WindowAppearance {
-    Light,
-    VibrantLight,
-    Dark,
-    VibrantDark,
-}
-
-impl Default for WindowAppearance {
-    fn default() -> Self {
-        Self::Light
-    }
-}
-
-#[derive(Copy, Clone, Debug)]
-pub struct PathPromptOptions {
-    pub files: bool,
-    pub directories: bool,
-    pub multiple: bool,
-}
-
-#[derive(Copy, Clone, Debug)]
-pub enum PromptLevel {
-    Info,
-    Warning,
-    Critical,
-}
-
-/// The style of the cursor (pointer)
-#[derive(Copy, Clone, Debug)]
-pub enum CursorStyle {
-    Arrow,
-    IBeam,
-    Crosshair,
-    ClosedHand,
-    OpenHand,
-    PointingHand,
-    ResizeLeft,
-    ResizeRight,
-    ResizeLeftRight,
-    ResizeUp,
-    ResizeDown,
-    ResizeUpDown,
-    DisappearingItem,
-    IBeamCursorForVerticalLayout,
-    OperationNotAllowed,
-    DragLink,
-    DragCopy,
-    ContextualMenu,
-}
-
-impl Default for CursorStyle {
-    fn default() -> Self {
-        Self::Arrow
-    }
-}
-
-#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Serialize)]
-pub struct SemanticVersion {
-    major: usize,
-    minor: usize,
-    patch: usize,
-}
-
-impl FromStr for SemanticVersion {
-    type Err = anyhow::Error;
-
-    fn from_str(s: &str) -> Result<Self> {
-        let mut components = s.trim().split('.');
-        let major = components
-            .next()
-            .ok_or_else(|| anyhow!("missing major version number"))?
-            .parse()?;
-        let minor = components
-            .next()
-            .ok_or_else(|| anyhow!("missing minor version number"))?
-            .parse()?;
-        let patch = components
-            .next()
-            .ok_or_else(|| anyhow!("missing patch version number"))?
-            .parse()?;
-        Ok(Self {
-            major,
-            minor,
-            patch,
-        })
-    }
-}
-
-impl Display for SemanticVersion {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
-    }
-}
-
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct ClipboardItem {
-    pub(crate) text: String,
-    pub(crate) metadata: Option<String>,
-}
-
-impl ClipboardItem {
-    pub fn new(text: String) -> Self {
-        Self {
-            text,
-            metadata: None,
-        }
-    }
-
-    pub fn with_metadata<T: Serialize>(mut self, metadata: T) -> Self {
-        self.metadata = Some(serde_json::to_string(&metadata).unwrap());
-        self
-    }
-
-    pub fn text(&self) -> &String {
-        &self.text
-    }
-
-    pub fn metadata<T>(&self) -> Option<T>
-    where
-        T: for<'a> Deserialize<'a>,
-    {
-        self.metadata
-            .as_ref()
-            .and_then(|m| serde_json::from_str(m).ok())
-    }
-
-    pub(crate) fn text_hash(text: &str) -> u64 {
-        let mut hasher = SeaHasher::new();
-        text.hash(&mut hasher);
-        hasher.finish()
-    }
-}

crates/gpui2/src/platform/mac.rs 🔗

@@ -1,139 +0,0 @@
-//! Macos screen have a y axis that goings up from the bottom of the screen and
-//! an origin at the bottom left of the main display.
-mod dispatcher;
-mod display;
-mod display_linker;
-mod events;
-mod metal_atlas;
-mod metal_renderer;
-mod open_type;
-mod platform;
-mod text_system;
-mod window;
-mod window_appearence;
-
-use crate::{px, size, GlobalPixels, Pixels, Size};
-use cocoa::{
-    base::{id, nil},
-    foundation::{NSAutoreleasePool, NSNotFound, NSRect, NSSize, NSString, NSUInteger},
-};
-use metal_renderer::*;
-use objc::runtime::{BOOL, NO, YES};
-use std::ops::Range;
-
-pub use dispatcher::*;
-pub use display::*;
-pub use display_linker::*;
-pub use metal_atlas::*;
-pub use platform::*;
-pub use text_system::*;
-pub use window::*;
-
-trait BoolExt {
-    fn to_objc(self) -> BOOL;
-}
-
-impl BoolExt for bool {
-    fn to_objc(self) -> BOOL {
-        if self {
-            YES
-        } else {
-            NO
-        }
-    }
-}
-
-#[repr(C)]
-#[derive(Copy, Clone, Debug)]
-struct NSRange {
-    pub location: NSUInteger,
-    pub length: NSUInteger,
-}
-
-impl NSRange {
-    fn invalid() -> Self {
-        Self {
-            location: NSNotFound as NSUInteger,
-            length: 0,
-        }
-    }
-
-    fn is_valid(&self) -> bool {
-        self.location != NSNotFound as NSUInteger
-    }
-
-    fn to_range(self) -> Option<Range<usize>> {
-        if self.is_valid() {
-            let start = self.location as usize;
-            let end = start + self.length as usize;
-            Some(start..end)
-        } else {
-            None
-        }
-    }
-}
-
-impl From<Range<usize>> for NSRange {
-    fn from(range: Range<usize>) -> Self {
-        NSRange {
-            location: range.start as NSUInteger,
-            length: range.len() as NSUInteger,
-        }
-    }
-}
-
-unsafe impl objc::Encode for NSRange {
-    fn encode() -> objc::Encoding {
-        let encoding = format!(
-            "{{NSRange={}{}}}",
-            NSUInteger::encode().as_str(),
-            NSUInteger::encode().as_str()
-        );
-        unsafe { objc::Encoding::from_str(&encoding) }
-    }
-}
-
-unsafe fn ns_string(string: &str) -> id {
-    NSString::alloc(nil).init_str(string).autorelease()
-}
-
-impl From<NSSize> for Size<Pixels> {
-    fn from(value: NSSize) -> Self {
-        Size {
-            width: px(value.width as f32),
-            height: px(value.height as f32),
-        }
-    }
-}
-
-pub trait NSRectExt {
-    fn size(&self) -> Size<Pixels>;
-    fn intersects(&self, other: Self) -> bool;
-}
-
-impl From<NSRect> for Size<Pixels> {
-    fn from(rect: NSRect) -> Self {
-        let NSSize { width, height } = rect.size;
-        size(width.into(), height.into())
-    }
-}
-
-impl From<NSRect> for Size<GlobalPixels> {
-    fn from(rect: NSRect) -> Self {
-        let NSSize { width, height } = rect.size;
-        size(width.into(), height.into())
-    }
-}
-
-// impl NSRectExt for NSRect {
-//     fn intersects(&self, other: Self) -> bool {
-//         self.size.width > 0.
-//             && self.size.height > 0.
-//             && other.size.width > 0.
-//             && other.size.height > 0.
-//             && self.origin.x <= other.origin.x + other.size.width
-//             && self.origin.x + self.size.width >= other.origin.x
-//             && self.origin.y <= other.origin.y + other.size.height
-//             && self.origin.y + self.size.height >= other.origin.y
-//     }
-// }

crates/gpui2/src/platform/mac/dispatcher.rs 🔗

@@ -1,96 +0,0 @@
-#![allow(non_upper_case_globals)]
-#![allow(non_camel_case_types)]
-#![allow(non_snake_case)]
-
-use crate::{PlatformDispatcher, TaskLabel};
-use async_task::Runnable;
-use objc::{
-    class, msg_send,
-    runtime::{BOOL, YES},
-    sel, sel_impl,
-};
-use parking::{Parker, Unparker};
-use parking_lot::Mutex;
-use std::{ffi::c_void, sync::Arc, time::Duration};
-
-include!(concat!(env!("OUT_DIR"), "/dispatch_sys.rs"));
-
-pub fn dispatch_get_main_queue() -> dispatch_queue_t {
-    unsafe { &_dispatch_main_q as *const _ as dispatch_queue_t }
-}
-
-pub struct MacDispatcher {
-    parker: Arc<Mutex<Parker>>,
-}
-
-impl Default for MacDispatcher {
-    fn default() -> Self {
-        Self::new()
-    }
-}
-
-impl MacDispatcher {
-    pub fn new() -> Self {
-        MacDispatcher {
-            parker: Arc::new(Mutex::new(Parker::new())),
-        }
-    }
-}
-
-impl PlatformDispatcher for MacDispatcher {
-    fn is_main_thread(&self) -> bool {
-        let is_main_thread: BOOL = unsafe { msg_send![class!(NSThread), isMainThread] };
-        is_main_thread == YES
-    }
-
-    fn dispatch(&self, runnable: Runnable, _: Option<TaskLabel>) {
-        unsafe {
-            dispatch_async_f(
-                dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT.try_into().unwrap(), 0),
-                runnable.into_raw() as *mut c_void,
-                Some(trampoline),
-            );
-        }
-    }
-
-    fn dispatch_on_main_thread(&self, runnable: Runnable) {
-        unsafe {
-            dispatch_async_f(
-                dispatch_get_main_queue(),
-                runnable.into_raw() as *mut c_void,
-                Some(trampoline),
-            );
-        }
-    }
-
-    fn dispatch_after(&self, duration: Duration, runnable: Runnable) {
-        unsafe {
-            let queue =
-                dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT.try_into().unwrap(), 0);
-            let when = dispatch_time(DISPATCH_TIME_NOW as u64, duration.as_nanos() as i64);
-            dispatch_after_f(
-                when,
-                queue,
-                runnable.into_raw() as *mut c_void,
-                Some(trampoline),
-            );
-        }
-    }
-
-    fn tick(&self, _background_only: bool) -> bool {
-        false
-    }
-
-    fn park(&self) {
-        self.parker.lock().park()
-    }
-
-    fn unparker(&self) -> Unparker {
-        self.parker.lock().unparker()
-    }
-}
-
-extern "C" fn trampoline(runnable: *mut c_void) {
-    let task = unsafe { Runnable::from_raw(runnable as *mut ()) };
-    task.run();
-}

crates/gpui2/src/platform/mac/platform.rs 🔗

@@ -1,1194 +0,0 @@
-use super::{events::key_to_native, BoolExt};
-use crate::{
-    Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId,
-    ForegroundExecutor, InputEvent, Keymap, MacDispatcher, MacDisplay, MacDisplayLinker,
-    MacTextSystem, MacWindow, Menu, MenuItem, PathPromptOptions, Platform, PlatformDisplay,
-    PlatformTextSystem, PlatformWindow, Result, Scene, SemanticVersion, VideoTimestamp,
-    WindowOptions,
-};
-use anyhow::anyhow;
-use block::ConcreteBlock;
-use cocoa::{
-    appkit::{
-        NSApplication, NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular,
-        NSEventModifierFlags, NSMenu, NSMenuItem, NSModalResponse, NSOpenPanel, NSPasteboard,
-        NSPasteboardTypeString, NSSavePanel, NSWindow,
-    },
-    base::{id, nil, selector, BOOL, YES},
-    foundation::{
-        NSArray, NSAutoreleasePool, NSBundle, NSData, NSInteger, NSProcessInfo, NSString,
-        NSUInteger, NSURL,
-    },
-};
-use core_foundation::{
-    base::{CFType, CFTypeRef, OSStatus, TCFType as _},
-    boolean::CFBoolean,
-    data::CFData,
-    dictionary::{CFDictionary, CFDictionaryRef, CFMutableDictionary},
-    string::{CFString, CFStringRef},
-};
-use ctor::ctor;
-use futures::channel::oneshot;
-use objc::{
-    class,
-    declare::ClassDecl,
-    msg_send,
-    runtime::{Class, Object, Sel},
-    sel, sel_impl,
-};
-use parking_lot::Mutex;
-use ptr::null_mut;
-use std::{
-    cell::Cell,
-    convert::TryInto,
-    ffi::{c_void, CStr, OsStr},
-    os::{raw::c_char, unix::ffi::OsStrExt},
-    path::{Path, PathBuf},
-    process::Command,
-    ptr,
-    rc::Rc,
-    slice, str,
-    sync::Arc,
-    time::Duration,
-};
-use time::UtcOffset;
-
-#[allow(non_upper_case_globals)]
-const NSUTF8StringEncoding: NSUInteger = 4;
-
-#[allow(non_upper_case_globals)]
-pub const NSViewLayerContentsRedrawDuringViewResize: NSInteger = 2;
-
-const MAC_PLATFORM_IVAR: &str = "platform";
-static mut APP_CLASS: *const Class = ptr::null();
-static mut APP_DELEGATE_CLASS: *const Class = ptr::null();
-
-#[ctor]
-unsafe fn build_classes() {
-    APP_CLASS = {
-        let mut decl = ClassDecl::new("GPUI2Application", class!(NSApplication)).unwrap();
-        decl.add_ivar::<*mut c_void>(MAC_PLATFORM_IVAR);
-        decl.add_method(
-            sel!(sendEvent:),
-            send_event as extern "C" fn(&mut Object, Sel, id),
-        );
-        decl.register()
-    };
-
-    APP_DELEGATE_CLASS = {
-        let mut decl = ClassDecl::new("GPUI2ApplicationDelegate", class!(NSResponder)).unwrap();
-        decl.add_ivar::<*mut c_void>(MAC_PLATFORM_IVAR);
-        decl.add_method(
-            sel!(applicationDidFinishLaunching:),
-            did_finish_launching as extern "C" fn(&mut Object, Sel, id),
-        );
-        decl.add_method(
-            sel!(applicationShouldHandleReopen:hasVisibleWindows:),
-            should_handle_reopen as extern "C" fn(&mut Object, Sel, id, bool),
-        );
-        decl.add_method(
-            sel!(applicationDidBecomeActive:),
-            did_become_active as extern "C" fn(&mut Object, Sel, id),
-        );
-        decl.add_method(
-            sel!(applicationDidResignActive:),
-            did_resign_active as extern "C" fn(&mut Object, Sel, id),
-        );
-        decl.add_method(
-            sel!(applicationWillTerminate:),
-            will_terminate as extern "C" fn(&mut Object, Sel, id),
-        );
-        decl.add_method(
-            sel!(handleGPUIMenuItem:),
-            handle_menu_item as extern "C" fn(&mut Object, Sel, id),
-        );
-        // Add menu item handlers so that OS save panels have the correct key commands
-        decl.add_method(
-            sel!(cut:),
-            handle_menu_item as extern "C" fn(&mut Object, Sel, id),
-        );
-        decl.add_method(
-            sel!(copy:),
-            handle_menu_item as extern "C" fn(&mut Object, Sel, id),
-        );
-        decl.add_method(
-            sel!(paste:),
-            handle_menu_item as extern "C" fn(&mut Object, Sel, id),
-        );
-        decl.add_method(
-            sel!(selectAll:),
-            handle_menu_item as extern "C" fn(&mut Object, Sel, id),
-        );
-        decl.add_method(
-            sel!(undo:),
-            handle_menu_item as extern "C" fn(&mut Object, Sel, id),
-        );
-        decl.add_method(
-            sel!(redo:),
-            handle_menu_item as extern "C" fn(&mut Object, Sel, id),
-        );
-        decl.add_method(
-            sel!(validateMenuItem:),
-            validate_menu_item as extern "C" fn(&mut Object, Sel, id) -> bool,
-        );
-        decl.add_method(
-            sel!(menuWillOpen:),
-            menu_will_open as extern "C" fn(&mut Object, Sel, id),
-        );
-        decl.add_method(
-            sel!(application:openURLs:),
-            open_urls as extern "C" fn(&mut Object, Sel, id, id),
-        );
-        decl.register()
-    }
-}
-
-pub struct MacPlatform(Mutex<MacPlatformState>);
-
-pub struct MacPlatformState {
-    background_executor: BackgroundExecutor,
-    foreground_executor: ForegroundExecutor,
-    text_system: Arc<MacTextSystem>,
-    display_linker: MacDisplayLinker,
-    pasteboard: id,
-    text_hash_pasteboard_type: id,
-    metadata_pasteboard_type: id,
-    become_active: Option<Box<dyn FnMut()>>,
-    resign_active: Option<Box<dyn FnMut()>>,
-    reopen: Option<Box<dyn FnMut()>>,
-    quit: Option<Box<dyn FnMut()>>,
-    event: Option<Box<dyn FnMut(InputEvent) -> bool>>,
-    menu_command: Option<Box<dyn FnMut(&dyn Action)>>,
-    validate_menu_command: Option<Box<dyn FnMut(&dyn Action) -> bool>>,
-    will_open_menu: Option<Box<dyn FnMut()>>,
-    menu_actions: Vec<Box<dyn Action>>,
-    open_urls: Option<Box<dyn FnMut(Vec<String>)>>,
-    finish_launching: Option<Box<dyn FnOnce()>>,
-}
-
-impl Default for MacPlatform {
-    fn default() -> Self {
-        Self::new()
-    }
-}
-
-impl MacPlatform {
-    pub fn new() -> Self {
-        let dispatcher = Arc::new(MacDispatcher::new());
-        Self(Mutex::new(MacPlatformState {
-            background_executor: BackgroundExecutor::new(dispatcher.clone()),
-            foreground_executor: ForegroundExecutor::new(dispatcher),
-            text_system: Arc::new(MacTextSystem::new()),
-            display_linker: MacDisplayLinker::new(),
-            pasteboard: unsafe { NSPasteboard::generalPasteboard(nil) },
-            text_hash_pasteboard_type: unsafe { ns_string("zed-text-hash") },
-            metadata_pasteboard_type: unsafe { ns_string("zed-metadata") },
-            become_active: None,
-            resign_active: None,
-            reopen: None,
-            quit: None,
-            event: None,
-            menu_command: None,
-            validate_menu_command: None,
-            will_open_menu: None,
-            menu_actions: Default::default(),
-            open_urls: None,
-            finish_launching: None,
-        }))
-    }
-
-    unsafe fn read_from_pasteboard(&self, pasteboard: *mut Object, kind: id) -> Option<&[u8]> {
-        let data = pasteboard.dataForType(kind);
-        if data == nil {
-            None
-        } else {
-            Some(slice::from_raw_parts(
-                data.bytes() as *mut u8,
-                data.length() as usize,
-            ))
-        }
-    }
-
-    unsafe fn create_menu_bar(
-        &self,
-        menus: Vec<Menu>,
-        delegate: id,
-        actions: &mut Vec<Box<dyn Action>>,
-        keymap: &Keymap,
-    ) -> id {
-        let application_menu = NSMenu::new(nil).autorelease();
-        application_menu.setDelegate_(delegate);
-
-        for menu_config in menus {
-            let menu = NSMenu::new(nil).autorelease();
-            menu.setTitle_(ns_string(menu_config.name));
-            menu.setDelegate_(delegate);
-
-            for item_config in menu_config.items {
-                menu.addItem_(Self::create_menu_item(
-                    item_config,
-                    delegate,
-                    actions,
-                    keymap,
-                ));
-            }
-
-            let menu_item = NSMenuItem::new(nil).autorelease();
-            menu_item.setSubmenu_(menu);
-            application_menu.addItem_(menu_item);
-
-            if menu_config.name == "Window" {
-                let app: id = msg_send![APP_CLASS, sharedApplication];
-                app.setWindowsMenu_(menu);
-            }
-        }
-
-        application_menu
-    }
-
-    unsafe fn create_menu_item(
-        item: MenuItem,
-        delegate: id,
-        actions: &mut Vec<Box<dyn Action>>,
-        keymap: &Keymap,
-    ) -> id {
-        match item {
-            MenuItem::Separator => NSMenuItem::separatorItem(nil),
-            MenuItem::Action {
-                name,
-                action,
-                os_action,
-            } => {
-                let keystrokes = keymap
-                    .bindings_for_action(action.type_id())
-                    .find(|binding| binding.action().partial_eq(action.as_ref()))
-                    .map(|binding| binding.keystrokes());
-
-                let selector = match os_action {
-                    Some(crate::OsAction::Cut) => selector("cut:"),
-                    Some(crate::OsAction::Copy) => selector("copy:"),
-                    Some(crate::OsAction::Paste) => selector("paste:"),
-                    Some(crate::OsAction::SelectAll) => selector("selectAll:"),
-                    Some(crate::OsAction::Undo) => selector("undo:"),
-                    Some(crate::OsAction::Redo) => selector("redo:"),
-                    None => selector("handleGPUIMenuItem:"),
-                };
-
-                let item;
-                if let Some(keystrokes) = keystrokes {
-                    if keystrokes.len() == 1 {
-                        let keystroke = &keystrokes[0];
-                        let mut mask = NSEventModifierFlags::empty();
-                        for (modifier, flag) in &[
-                            (
-                                keystroke.modifiers.command,
-                                NSEventModifierFlags::NSCommandKeyMask,
-                            ),
-                            (
-                                keystroke.modifiers.control,
-                                NSEventModifierFlags::NSControlKeyMask,
-                            ),
-                            (
-                                keystroke.modifiers.alt,
-                                NSEventModifierFlags::NSAlternateKeyMask,
-                            ),
-                            (
-                                keystroke.modifiers.shift,
-                                NSEventModifierFlags::NSShiftKeyMask,
-                            ),
-                        ] {
-                            if *modifier {
-                                mask |= *flag;
-                            }
-                        }
-
-                        item = NSMenuItem::alloc(nil)
-                            .initWithTitle_action_keyEquivalent_(
-                                ns_string(name),
-                                selector,
-                                ns_string(key_to_native(&keystroke.key).as_ref()),
-                            )
-                            .autorelease();
-                        item.setKeyEquivalentModifierMask_(mask);
-                    }
-                    // For multi-keystroke bindings, render the keystroke as part of the title.
-                    else {
-                        use std::fmt::Write;
-
-                        let mut name = format!("{name} [");
-                        for (i, keystroke) in keystrokes.iter().enumerate() {
-                            if i > 0 {
-                                name.push(' ');
-                            }
-                            write!(&mut name, "{}", keystroke).unwrap();
-                        }
-                        name.push(']');
-
-                        item = NSMenuItem::alloc(nil)
-                            .initWithTitle_action_keyEquivalent_(
-                                ns_string(&name),
-                                selector,
-                                ns_string(""),
-                            )
-                            .autorelease();
-                    }
-                } else {
-                    item = NSMenuItem::alloc(nil)
-                        .initWithTitle_action_keyEquivalent_(
-                            ns_string(name),
-                            selector,
-                            ns_string(""),
-                        )
-                        .autorelease();
-                }
-
-                let tag = actions.len() as NSInteger;
-                let _: () = msg_send![item, setTag: tag];
-                actions.push(action);
-                item
-            }
-            MenuItem::Submenu(Menu { name, items }) => {
-                let item = NSMenuItem::new(nil).autorelease();
-                let submenu = NSMenu::new(nil).autorelease();
-                submenu.setDelegate_(delegate);
-                for item in items {
-                    submenu.addItem_(Self::create_menu_item(item, delegate, actions, keymap));
-                }
-                item.setSubmenu_(submenu);
-                item.setTitle_(ns_string(name));
-                item
-            }
-        }
-    }
-}
-
-impl Platform for MacPlatform {
-    fn background_executor(&self) -> BackgroundExecutor {
-        self.0.lock().background_executor.clone()
-    }
-
-    fn foreground_executor(&self) -> crate::ForegroundExecutor {
-        self.0.lock().foreground_executor.clone()
-    }
-
-    fn text_system(&self) -> Arc<dyn PlatformTextSystem> {
-        self.0.lock().text_system.clone()
-    }
-
-    fn run(&self, on_finish_launching: Box<dyn FnOnce()>) {
-        self.0.lock().finish_launching = Some(on_finish_launching);
-
-        unsafe {
-            let app: id = msg_send![APP_CLASS, sharedApplication];
-            let app_delegate: id = msg_send![APP_DELEGATE_CLASS, new];
-            app.setDelegate_(app_delegate);
-
-            let self_ptr = self as *const Self as *const c_void;
-            (*app).set_ivar(MAC_PLATFORM_IVAR, self_ptr);
-            (*app_delegate).set_ivar(MAC_PLATFORM_IVAR, self_ptr);
-
-            let pool = NSAutoreleasePool::new(nil);
-            app.run();
-            pool.drain();
-
-            (*app).set_ivar(MAC_PLATFORM_IVAR, null_mut::<c_void>());
-            (*app.delegate()).set_ivar(MAC_PLATFORM_IVAR, null_mut::<c_void>());
-        }
-    }
-
-    fn quit(&self) {
-        // Quitting the app causes us to close windows, which invokes `Window::on_close` callbacks
-        // synchronously before this method terminates. If we call `Platform::quit` while holding a
-        // borrow of the app state (which most of the time we will do), we will end up
-        // double-borrowing the app state in the `on_close` callbacks for our open windows. To solve
-        // this, we make quitting the application asynchronous so that we aren't holding borrows to
-        // the app state on the stack when we actually terminate the app.
-
-        use super::dispatcher::{dispatch_async_f, dispatch_get_main_queue};
-
-        unsafe {
-            dispatch_async_f(dispatch_get_main_queue(), ptr::null_mut(), Some(quit));
-        }
-
-        unsafe extern "C" fn quit(_: *mut c_void) {
-            let app = NSApplication::sharedApplication(nil);
-            let _: () = msg_send![app, terminate: nil];
-        }
-    }
-
-    fn restart(&self) {
-        use std::os::unix::process::CommandExt as _;
-
-        let app_pid = std::process::id().to_string();
-        let app_path = self
-            .app_path()
-            .ok()
-            // When the app is not bundled, `app_path` returns the
-            // directory containing the executable. Disregard this
-            // and get the path to the executable itself.
-            .and_then(|path| (path.extension()?.to_str()? == "app").then_some(path))
-            .unwrap_or_else(|| std::env::current_exe().unwrap());
-
-        // Wait until this process has exited and then re-open this path.
-        let script = r#"
-            while kill -0 $0 2> /dev/null; do
-                sleep 0.1
-            done
-            open "$1"
-        "#;
-
-        let restart_process = Command::new("/bin/bash")
-            .arg("-c")
-            .arg(script)
-            .arg(app_pid)
-            .arg(app_path)
-            .process_group(0)
-            .spawn();
-
-        match restart_process {
-            Ok(_) => self.quit(),
-            Err(e) => log::error!("failed to spawn restart script: {:?}", e),
-        }
-    }
-
-    fn activate(&self, ignoring_other_apps: bool) {
-        unsafe {
-            let app = NSApplication::sharedApplication(nil);
-            app.activateIgnoringOtherApps_(ignoring_other_apps.to_objc());
-        }
-    }
-
-    fn hide(&self) {
-        unsafe {
-            let app = NSApplication::sharedApplication(nil);
-            let _: () = msg_send![app, hide: nil];
-        }
-    }
-
-    fn hide_other_apps(&self) {
-        unsafe {
-            let app = NSApplication::sharedApplication(nil);
-            let _: () = msg_send![app, hideOtherApplications: nil];
-        }
-    }
-
-    fn unhide_other_apps(&self) {
-        unsafe {
-            let app = NSApplication::sharedApplication(nil);
-            let _: () = msg_send![app, unhideAllApplications: nil];
-        }
-    }
-
-    // fn add_status_item(&self, _handle: AnyWindowHandle) -> Box<dyn platform::Window> {
-    //     Box::new(StatusItem::add(self.fonts()))
-    // }
-
-    fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
-        MacDisplay::all()
-            .map(|screen| Rc::new(screen) as Rc<_>)
-            .collect()
-    }
-
-    fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
-        MacDisplay::find_by_id(id).map(|screen| Rc::new(screen) as Rc<_>)
-    }
-
-    fn active_window(&self) -> Option<AnyWindowHandle> {
-        MacWindow::active_window()
-    }
-
-    fn open_window(
-        &self,
-        handle: AnyWindowHandle,
-        options: WindowOptions,
-        draw: Box<dyn FnMut() -> Result<Scene>>,
-    ) -> Box<dyn PlatformWindow> {
-        Box::new(MacWindow::open(
-            handle,
-            options,
-            draw,
-            self.foreground_executor(),
-        ))
-    }
-
-    fn set_display_link_output_callback(
-        &self,
-        display_id: DisplayId,
-        callback: Box<dyn FnMut(&VideoTimestamp, &VideoTimestamp) + Send>,
-    ) {
-        self.0
-            .lock()
-            .display_linker
-            .set_output_callback(display_id, callback);
-    }
-
-    fn start_display_link(&self, display_id: DisplayId) {
-        self.0.lock().display_linker.start(display_id);
-    }
-
-    fn stop_display_link(&self, display_id: DisplayId) {
-        self.0.lock().display_linker.stop(display_id);
-    }
-
-    fn open_url(&self, url: &str) {
-        unsafe {
-            let url = NSURL::alloc(nil)
-                .initWithString_(ns_string(url))
-                .autorelease();
-            let workspace: id = msg_send![class!(NSWorkspace), sharedWorkspace];
-            msg_send![workspace, openURL: url]
-        }
-    }
-
-    fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>) {
-        self.0.lock().open_urls = Some(callback);
-    }
-
-    fn prompt_for_paths(
-        &self,
-        options: PathPromptOptions,
-    ) -> oneshot::Receiver<Option<Vec<PathBuf>>> {
-        unsafe {
-            let panel = NSOpenPanel::openPanel(nil);
-            panel.setCanChooseDirectories_(options.directories.to_objc());
-            panel.setCanChooseFiles_(options.files.to_objc());
-            panel.setAllowsMultipleSelection_(options.multiple.to_objc());
-            panel.setResolvesAliases_(false.to_objc());
-            let (done_tx, done_rx) = oneshot::channel();
-            let done_tx = Cell::new(Some(done_tx));
-            let block = ConcreteBlock::new(move |response: NSModalResponse| {
-                let result = if response == NSModalResponse::NSModalResponseOk {
-                    let mut result = Vec::new();
-                    let urls = panel.URLs();
-                    for i in 0..urls.count() {
-                        let url = urls.objectAtIndex(i);
-                        if url.isFileURL() == YES {
-                            if let Ok(path) = ns_url_to_path(url) {
-                                result.push(path)
-                            }
-                        }
-                    }
-                    Some(result)
-                } else {
-                    None
-                };
-
-                if let Some(done_tx) = done_tx.take() {
-                    let _ = done_tx.send(result);
-                }
-            });
-            let block = block.copy();
-            let _: () = msg_send![panel, beginWithCompletionHandler: block];
-            done_rx
-        }
-    }
-
-    fn prompt_for_new_path(&self, directory: &Path) -> oneshot::Receiver<Option<PathBuf>> {
-        unsafe {
-            let panel = NSSavePanel::savePanel(nil);
-            let path = ns_string(directory.to_string_lossy().as_ref());
-            let url = NSURL::fileURLWithPath_isDirectory_(nil, path, true.to_objc());
-            panel.setDirectoryURL(url);
-
-            let (done_tx, done_rx) = oneshot::channel();
-            let done_tx = Cell::new(Some(done_tx));
-            let block = ConcreteBlock::new(move |response: NSModalResponse| {
-                let mut result = None;
-                if response == NSModalResponse::NSModalResponseOk {
-                    let url = panel.URL();
-                    if url.isFileURL() == YES {
-                        result = ns_url_to_path(panel.URL()).ok()
-                    }
-                }
-
-                if let Some(done_tx) = done_tx.take() {
-                    let _ = done_tx.send(result);
-                }
-            });
-            let block = block.copy();
-            let _: () = msg_send![panel, beginWithCompletionHandler: block];
-            done_rx
-        }
-    }
-
-    fn reveal_path(&self, path: &Path) {
-        unsafe {
-            let path = path.to_path_buf();
-            self.0
-                .lock()
-                .background_executor
-                .spawn(async move {
-                    let full_path = ns_string(path.to_str().unwrap_or(""));
-                    let root_full_path = ns_string("");
-                    let workspace: id = msg_send![class!(NSWorkspace), sharedWorkspace];
-                    let _: BOOL = msg_send![
-                        workspace,
-                        selectFile: full_path
-                        inFileViewerRootedAtPath: root_full_path
-                    ];
-                })
-                .detach();
-        }
-    }
-
-    fn on_become_active(&self, callback: Box<dyn FnMut()>) {
-        self.0.lock().become_active = Some(callback);
-    }
-
-    fn on_resign_active(&self, callback: Box<dyn FnMut()>) {
-        self.0.lock().resign_active = Some(callback);
-    }
-
-    fn on_quit(&self, callback: Box<dyn FnMut()>) {
-        self.0.lock().quit = Some(callback);
-    }
-
-    fn on_reopen(&self, callback: Box<dyn FnMut()>) {
-        self.0.lock().reopen = Some(callback);
-    }
-
-    fn on_event(&self, callback: Box<dyn FnMut(InputEvent) -> bool>) {
-        self.0.lock().event = Some(callback);
-    }
-
-    fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>) {
-        self.0.lock().menu_command = Some(callback);
-    }
-
-    fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>) {
-        self.0.lock().will_open_menu = Some(callback);
-    }
-
-    fn on_validate_app_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>) {
-        self.0.lock().validate_menu_command = Some(callback);
-    }
-
-    fn os_name(&self) -> &'static str {
-        "macOS"
-    }
-
-    fn double_click_interval(&self) -> Duration {
-        unsafe {
-            let double_click_interval: f64 = msg_send![class!(NSEvent), doubleClickInterval];
-            Duration::from_secs_f64(double_click_interval)
-        }
-    }
-
-    fn os_version(&self) -> Result<SemanticVersion> {
-        unsafe {
-            let process_info = NSProcessInfo::processInfo(nil);
-            let version = process_info.operatingSystemVersion();
-            Ok(SemanticVersion {
-                major: version.majorVersion as usize,
-                minor: version.minorVersion as usize,
-                patch: version.patchVersion as usize,
-            })
-        }
-    }
-
-    fn app_version(&self) -> Result<SemanticVersion> {
-        unsafe {
-            let bundle: id = NSBundle::mainBundle();
-            if bundle.is_null() {
-                Err(anyhow!("app is not running inside a bundle"))
-            } else {
-                let version: id = msg_send![bundle, objectForInfoDictionaryKey: ns_string("CFBundleShortVersionString")];
-                let len = msg_send![version, lengthOfBytesUsingEncoding: NSUTF8StringEncoding];
-                let bytes = version.UTF8String() as *const u8;
-                let version = str::from_utf8(slice::from_raw_parts(bytes, len)).unwrap();
-                version.parse()
-            }
-        }
-    }
-
-    fn app_path(&self) -> Result<PathBuf> {
-        unsafe {
-            let bundle: id = NSBundle::mainBundle();
-            if bundle.is_null() {
-                Err(anyhow!("app is not running inside a bundle"))
-            } else {
-                Ok(path_from_objc(msg_send![bundle, bundlePath]))
-            }
-        }
-    }
-
-    fn set_menus(&self, menus: Vec<Menu>, keymap: &Keymap) {
-        unsafe {
-            let app: id = msg_send![APP_CLASS, sharedApplication];
-            let mut state = self.0.lock();
-            let actions = &mut state.menu_actions;
-            app.setMainMenu_(self.create_menu_bar(menus, app.delegate(), actions, keymap));
-        }
-    }
-
-    fn local_timezone(&self) -> UtcOffset {
-        unsafe {
-            let local_timezone: id = msg_send![class!(NSTimeZone), localTimeZone];
-            let seconds_from_gmt: NSInteger = msg_send![local_timezone, secondsFromGMT];
-            UtcOffset::from_whole_seconds(seconds_from_gmt.try_into().unwrap()).unwrap()
-        }
-    }
-
-    fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
-        unsafe {
-            let bundle: id = NSBundle::mainBundle();
-            if bundle.is_null() {
-                Err(anyhow!("app is not running inside a bundle"))
-            } else {
-                let name = ns_string(name);
-                let url: id = msg_send![bundle, URLForAuxiliaryExecutable: name];
-                if url.is_null() {
-                    Err(anyhow!("resource not found"))
-                } else {
-                    ns_url_to_path(url)
-                }
-            }
-        }
-    }
-
-    /// Match cursor style to one of the styles available
-    /// in macOS's [NSCursor](https://developer.apple.com/documentation/appkit/nscursor).
-    fn set_cursor_style(&self, style: CursorStyle) {
-        unsafe {
-            let new_cursor: id = match style {
-                CursorStyle::Arrow => msg_send![class!(NSCursor), arrowCursor],
-                CursorStyle::IBeam => msg_send![class!(NSCursor), IBeamCursor],
-                CursorStyle::Crosshair => msg_send![class!(NSCursor), crosshairCursor],
-                CursorStyle::ClosedHand => msg_send![class!(NSCursor), closedHandCursor],
-                CursorStyle::OpenHand => msg_send![class!(NSCursor), openHandCursor],
-                CursorStyle::PointingHand => msg_send![class!(NSCursor), pointingHandCursor],
-                CursorStyle::ResizeLeft => msg_send![class!(NSCursor), resizeLeftCursor],
-                CursorStyle::ResizeRight => msg_send![class!(NSCursor), resizeRightCursor],
-                CursorStyle::ResizeLeftRight => msg_send![class!(NSCursor), resizeLeftRightCursor],
-                CursorStyle::ResizeUp => msg_send![class!(NSCursor), resizeUpCursor],
-                CursorStyle::ResizeDown => msg_send![class!(NSCursor), resizeDownCursor],
-                CursorStyle::ResizeUpDown => msg_send![class!(NSCursor), resizeUpDownCursor],
-                CursorStyle::DisappearingItem => {
-                    msg_send![class!(NSCursor), disappearingItemCursor]
-                }
-                CursorStyle::IBeamCursorForVerticalLayout => {
-                    msg_send![class!(NSCursor), IBeamCursorForVerticalLayout]
-                }
-                CursorStyle::OperationNotAllowed => {
-                    msg_send![class!(NSCursor), operationNotAllowedCursor]
-                }
-                CursorStyle::DragLink => msg_send![class!(NSCursor), dragLinkCursor],
-                CursorStyle::DragCopy => msg_send![class!(NSCursor), dragCopyCursor],
-                CursorStyle::ContextualMenu => msg_send![class!(NSCursor), contextualMenuCursor],
-            };
-
-            let old_cursor: id = msg_send![class!(NSCursor), currentCursor];
-            if new_cursor != old_cursor {
-                let _: () = msg_send![new_cursor, set];
-            }
-        }
-    }
-
-    fn should_auto_hide_scrollbars(&self) -> bool {
-        #[allow(non_upper_case_globals)]
-        const NSScrollerStyleOverlay: NSInteger = 1;
-
-        unsafe {
-            let style: NSInteger = msg_send![class!(NSScroller), preferredScrollerStyle];
-            style == NSScrollerStyleOverlay
-        }
-    }
-
-    fn write_to_clipboard(&self, item: ClipboardItem) {
-        let state = self.0.lock();
-        unsafe {
-            state.pasteboard.clearContents();
-
-            let text_bytes = NSData::dataWithBytes_length_(
-                nil,
-                item.text.as_ptr() as *const c_void,
-                item.text.len() as u64,
-            );
-            state
-                .pasteboard
-                .setData_forType(text_bytes, NSPasteboardTypeString);
-
-            if let Some(metadata) = item.metadata.as_ref() {
-                let hash_bytes = ClipboardItem::text_hash(&item.text).to_be_bytes();
-                let hash_bytes = NSData::dataWithBytes_length_(
-                    nil,
-                    hash_bytes.as_ptr() as *const c_void,
-                    hash_bytes.len() as u64,
-                );
-                state
-                    .pasteboard
-                    .setData_forType(hash_bytes, state.text_hash_pasteboard_type);
-
-                let metadata_bytes = NSData::dataWithBytes_length_(
-                    nil,
-                    metadata.as_ptr() as *const c_void,
-                    metadata.len() as u64,
-                );
-                state
-                    .pasteboard
-                    .setData_forType(metadata_bytes, state.metadata_pasteboard_type);
-            }
-        }
-    }
-
-    fn read_from_clipboard(&self) -> Option<ClipboardItem> {
-        let state = self.0.lock();
-        unsafe {
-            if let Some(text_bytes) =
-                self.read_from_pasteboard(state.pasteboard, NSPasteboardTypeString)
-            {
-                let text = String::from_utf8_lossy(text_bytes).to_string();
-                let hash_bytes = self
-                    .read_from_pasteboard(state.pasteboard, state.text_hash_pasteboard_type)
-                    .and_then(|bytes| bytes.try_into().ok())
-                    .map(u64::from_be_bytes);
-                let metadata_bytes = self
-                    .read_from_pasteboard(state.pasteboard, state.metadata_pasteboard_type)
-                    .and_then(|bytes| String::from_utf8(bytes.to_vec()).ok());
-
-                if let Some((hash, metadata)) = hash_bytes.zip(metadata_bytes) {
-                    if hash == ClipboardItem::text_hash(&text) {
-                        Some(ClipboardItem {
-                            text,
-                            metadata: Some(metadata),
-                        })
-                    } else {
-                        Some(ClipboardItem {
-                            text,
-                            metadata: None,
-                        })
-                    }
-                } else {
-                    Some(ClipboardItem {
-                        text,
-                        metadata: None,
-                    })
-                }
-            } else {
-                None
-            }
-        }
-    }
-
-    fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Result<()> {
-        let url = CFString::from(url);
-        let username = CFString::from(username);
-        let password = CFData::from_buffer(password);
-
-        unsafe {
-            use security::*;
-
-            // First, check if there are already credentials for the given server. If so, then
-            // update the username and password.
-            let mut verb = "updating";
-            let mut query_attrs = CFMutableDictionary::with_capacity(2);
-            query_attrs.set(kSecClass as *const _, kSecClassInternetPassword as *const _);
-            query_attrs.set(kSecAttrServer as *const _, url.as_CFTypeRef());
-
-            let mut attrs = CFMutableDictionary::with_capacity(4);
-            attrs.set(kSecClass as *const _, kSecClassInternetPassword as *const _);
-            attrs.set(kSecAttrServer as *const _, url.as_CFTypeRef());
-            attrs.set(kSecAttrAccount as *const _, username.as_CFTypeRef());
-            attrs.set(kSecValueData as *const _, password.as_CFTypeRef());
-
-            let mut status = SecItemUpdate(
-                query_attrs.as_concrete_TypeRef(),
-                attrs.as_concrete_TypeRef(),
-            );
-
-            // If there were no existing credentials for the given server, then create them.
-            if status == errSecItemNotFound {
-                verb = "creating";
-                status = SecItemAdd(attrs.as_concrete_TypeRef(), ptr::null_mut());
-            }
-
-            if status != errSecSuccess {
-                return Err(anyhow!("{} password failed: {}", verb, status));
-            }
-        }
-        Ok(())
-    }
-
-    fn read_credentials(&self, url: &str) -> Result<Option<(String, Vec<u8>)>> {
-        let url = CFString::from(url);
-        let cf_true = CFBoolean::true_value().as_CFTypeRef();
-
-        unsafe {
-            use security::*;
-
-            // Find any credentials for the given server URL.
-            let mut attrs = CFMutableDictionary::with_capacity(5);
-            attrs.set(kSecClass as *const _, kSecClassInternetPassword as *const _);
-            attrs.set(kSecAttrServer as *const _, url.as_CFTypeRef());
-            attrs.set(kSecReturnAttributes as *const _, cf_true);
-            attrs.set(kSecReturnData as *const _, cf_true);
-
-            let mut result = CFTypeRef::from(ptr::null());
-            let status = SecItemCopyMatching(attrs.as_concrete_TypeRef(), &mut result);
-            match status {
-                security::errSecSuccess => {}
-                security::errSecItemNotFound | security::errSecUserCanceled => return Ok(None),
-                _ => return Err(anyhow!("reading password failed: {}", status)),
-            }
-
-            let result = CFType::wrap_under_create_rule(result)
-                .downcast::<CFDictionary>()
-                .ok_or_else(|| anyhow!("keychain item was not a dictionary"))?;
-            let username = result
-                .find(kSecAttrAccount as *const _)
-                .ok_or_else(|| anyhow!("account was missing from keychain item"))?;
-            let username = CFType::wrap_under_get_rule(*username)
-                .downcast::<CFString>()
-                .ok_or_else(|| anyhow!("account was not a string"))?;
-            let password = result
-                .find(kSecValueData as *const _)
-                .ok_or_else(|| anyhow!("password was missing from keychain item"))?;
-            let password = CFType::wrap_under_get_rule(*password)
-                .downcast::<CFData>()
-                .ok_or_else(|| anyhow!("password was not a string"))?;
-
-            Ok(Some((username.to_string(), password.bytes().to_vec())))
-        }
-    }
-
-    fn delete_credentials(&self, url: &str) -> Result<()> {
-        let url = CFString::from(url);
-
-        unsafe {
-            use security::*;
-
-            let mut query_attrs = CFMutableDictionary::with_capacity(2);
-            query_attrs.set(kSecClass as *const _, kSecClassInternetPassword as *const _);
-            query_attrs.set(kSecAttrServer as *const _, url.as_CFTypeRef());
-
-            let status = SecItemDelete(query_attrs.as_concrete_TypeRef());
-
-            if status != errSecSuccess {
-                return Err(anyhow!("delete password failed: {}", status));
-            }
-        }
-        Ok(())
-    }
-}
-
-unsafe fn path_from_objc(path: id) -> PathBuf {
-    let len = msg_send![path, lengthOfBytesUsingEncoding: NSUTF8StringEncoding];
-    let bytes = path.UTF8String() as *const u8;
-    let path = str::from_utf8(slice::from_raw_parts(bytes, len)).unwrap();
-    PathBuf::from(path)
-}
-
-unsafe fn get_mac_platform(object: &mut Object) -> &MacPlatform {
-    let platform_ptr: *mut c_void = *object.get_ivar(MAC_PLATFORM_IVAR);
-    assert!(!platform_ptr.is_null());
-    &*(platform_ptr as *const MacPlatform)
-}
-
-extern "C" fn send_event(this: &mut Object, _sel: Sel, native_event: id) {
-    unsafe {
-        if let Some(event) = InputEvent::from_native(native_event, None) {
-            let platform = get_mac_platform(this);
-            if let Some(callback) = platform.0.lock().event.as_mut() {
-                if !callback(event) {
-                    return;
-                }
-            }
-        }
-        msg_send![super(this, class!(NSApplication)), sendEvent: native_event]
-    }
-}
-
-extern "C" fn did_finish_launching(this: &mut Object, _: Sel, _: id) {
-    unsafe {
-        let app: id = msg_send![APP_CLASS, sharedApplication];
-        app.setActivationPolicy_(NSApplicationActivationPolicyRegular);
-
-        let platform = get_mac_platform(this);
-        let callback = platform.0.lock().finish_launching.take();
-        if let Some(callback) = callback {
-            callback();
-        }
-    }
-}
-
-extern "C" fn should_handle_reopen(this: &mut Object, _: Sel, _: id, has_open_windows: bool) {
-    if !has_open_windows {
-        let platform = unsafe { get_mac_platform(this) };
-        if let Some(callback) = platform.0.lock().reopen.as_mut() {
-            callback();
-        }
-    }
-}
-
-extern "C" fn did_become_active(this: &mut Object, _: Sel, _: id) {
-    let platform = unsafe { get_mac_platform(this) };
-    if let Some(callback) = platform.0.lock().become_active.as_mut() {
-        callback();
-    }
-}
-
-extern "C" fn did_resign_active(this: &mut Object, _: Sel, _: id) {
-    let platform = unsafe { get_mac_platform(this) };
-    if let Some(callback) = platform.0.lock().resign_active.as_mut() {
-        callback();
-    }
-}
-
-extern "C" fn will_terminate(this: &mut Object, _: Sel, _: id) {
-    let platform = unsafe { get_mac_platform(this) };
-    if let Some(callback) = platform.0.lock().quit.as_mut() {
-        callback();
-    }
-}
-
-extern "C" fn open_urls(this: &mut Object, _: Sel, _: id, urls: id) {
-    let urls = unsafe {
-        (0..urls.count())
-            .filter_map(|i| {
-                let url = urls.objectAtIndex(i);
-                match CStr::from_ptr(url.absoluteString().UTF8String() as *mut c_char).to_str() {
-                    Ok(string) => Some(string.to_string()),
-                    Err(err) => {
-                        log::error!("error converting path to string: {}", err);
-                        None
-                    }
-                }
-            })
-            .collect::<Vec<_>>()
-    };
-    let platform = unsafe { get_mac_platform(this) };
-    if let Some(callback) = platform.0.lock().open_urls.as_mut() {
-        callback(urls);
-    }
-}
-
-extern "C" fn handle_menu_item(this: &mut Object, _: Sel, item: id) {
-    unsafe {
-        let platform = get_mac_platform(this);
-        let mut platform = platform.0.lock();
-        if let Some(mut callback) = platform.menu_command.take() {
-            let tag: NSInteger = msg_send![item, tag];
-            let index = tag as usize;
-            if let Some(action) = platform.menu_actions.get(index) {
-                callback(action.as_ref());
-            }
-            platform.menu_command = Some(callback);
-        }
-    }
-}
-
-extern "C" fn validate_menu_item(this: &mut Object, _: Sel, item: id) -> bool {
-    unsafe {
-        let mut result = false;
-        let platform = get_mac_platform(this);
-        let mut platform = platform.0.lock();
-        if let Some(mut callback) = platform.validate_menu_command.take() {
-            let tag: NSInteger = msg_send![item, tag];
-            let index = tag as usize;
-            if let Some(action) = platform.menu_actions.get(index) {
-                result = callback(action.as_ref());
-            }
-            platform.validate_menu_command = Some(callback);
-        }
-        result
-    }
-}
-
-extern "C" fn menu_will_open(this: &mut Object, _: Sel, _: id) {
-    unsafe {
-        let platform = get_mac_platform(this);
-        let mut platform = platform.0.lock();
-        if let Some(mut callback) = platform.will_open_menu.take() {
-            callback();
-            platform.will_open_menu = Some(callback);
-        }
-    }
-}
-
-unsafe fn ns_string(string: &str) -> id {
-    NSString::alloc(nil).init_str(string).autorelease()
-}
-
-unsafe fn ns_url_to_path(url: id) -> Result<PathBuf> {
-    let path: *mut c_char = msg_send![url, fileSystemRepresentation];
-    if path.is_null() {
-        Err(anyhow!(
-            "url is not a file path: {}",
-            CStr::from_ptr(url.absoluteString().UTF8String()).to_string_lossy()
-        ))
-    } else {
-        Ok(PathBuf::from(OsStr::from_bytes(
-            CStr::from_ptr(path).to_bytes(),
-        )))
-    }
-}
-
-mod security {
-    #![allow(non_upper_case_globals)]
-    use super::*;
-
-    #[link(name = "Security", kind = "framework")]
-    extern "C" {
-        pub static kSecClass: CFStringRef;
-        pub static kSecClassInternetPassword: CFStringRef;
-        pub static kSecAttrServer: CFStringRef;
-        pub static kSecAttrAccount: CFStringRef;
-        pub static kSecValueData: CFStringRef;
-        pub static kSecReturnAttributes: CFStringRef;
-        pub static kSecReturnData: CFStringRef;
-
-        pub fn SecItemAdd(attributes: CFDictionaryRef, result: *mut CFTypeRef) -> OSStatus;
-        pub fn SecItemUpdate(query: CFDictionaryRef, attributes: CFDictionaryRef) -> OSStatus;
-        pub fn SecItemDelete(query: CFDictionaryRef) -> OSStatus;
-        pub fn SecItemCopyMatching(query: CFDictionaryRef, result: *mut CFTypeRef) -> OSStatus;
-    }
-
-    pub const errSecSuccess: OSStatus = 0;
-    pub const errSecUserCanceled: OSStatus = -128;
-    pub const errSecItemNotFound: OSStatus = -25300;
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::ClipboardItem;
-
-    use super::*;
-
-    #[test]
-    fn test_clipboard() {
-        let platform = build_platform();
-        assert_eq!(platform.read_from_clipboard(), None);
-
-        let item = ClipboardItem::new("1".to_string());
-        platform.write_to_clipboard(item.clone());
-        assert_eq!(platform.read_from_clipboard(), Some(item));
-
-        let item = ClipboardItem::new("2".to_string()).with_metadata(vec![3, 4]);
-        platform.write_to_clipboard(item.clone());
-        assert_eq!(platform.read_from_clipboard(), Some(item));
-
-        let text_from_other_app = "text from other app";
-        unsafe {
-            let bytes = NSData::dataWithBytes_length_(
-                nil,
-                text_from_other_app.as_ptr() as *const c_void,
-                text_from_other_app.len() as u64,
-            );
-            platform
-                .0
-                .lock()
-                .pasteboard
-                .setData_forType(bytes, NSPasteboardTypeString);
-        }
-        assert_eq!(
-            platform.read_from_clipboard(),
-            Some(ClipboardItem::new(text_from_other_app.to_string()))
-        );
-    }
-
-    fn build_platform() -> MacPlatform {
-        let platform = MacPlatform::new();
-        platform.0.lock().pasteboard = unsafe { NSPasteboard::pasteboardWithUniqueName(nil) };
-        platform
-    }
-}

crates/gpui2/src/platform/mac/window.rs 🔗

@@ -1,1791 +0,0 @@
-use super::{display_bounds_from_native, ns_string, MacDisplay, MetalRenderer, NSRange};
-use crate::{
-    display_bounds_to_native, point, px, size, AnyWindowHandle, Bounds, DrawWindow, ExternalPaths,
-    FileDropEvent, ForegroundExecutor, GlobalPixels, InputEvent, KeyDownEvent, Keystroke,
-    Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
-    Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point,
-    PromptLevel, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions,
-};
-use block::ConcreteBlock;
-use cocoa::{
-    appkit::{
-        CGPoint, NSApplication, NSBackingStoreBuffered, NSEventModifierFlags,
-        NSFilenamesPboardType, NSPasteboard, NSScreen, NSView, NSViewHeightSizable,
-        NSViewWidthSizable, NSWindow, NSWindowButton, NSWindowCollectionBehavior,
-        NSWindowStyleMask, NSWindowTitleVisibility,
-    },
-    base::{id, nil},
-    foundation::{
-        NSArray, NSAutoreleasePool, NSDictionary, NSFastEnumeration, NSInteger, NSPoint, NSRect,
-        NSSize, NSString, NSUInteger,
-    },
-};
-use core_graphics::display::CGRect;
-use ctor::ctor;
-use foreign_types::ForeignTypeRef;
-use futures::channel::oneshot;
-use objc::{
-    class,
-    declare::ClassDecl,
-    msg_send,
-    runtime::{Class, Object, Protocol, Sel, BOOL, NO, YES},
-    sel, sel_impl,
-};
-use parking_lot::Mutex;
-use smallvec::SmallVec;
-use std::{
-    any::Any,
-    cell::{Cell, RefCell},
-    ffi::{c_void, CStr},
-    mem,
-    ops::Range,
-    os::raw::c_char,
-    path::PathBuf,
-    ptr,
-    rc::Rc,
-    sync::{Arc, Weak},
-    time::Duration,
-};
-use util::ResultExt;
-
-const WINDOW_STATE_IVAR: &str = "windowState";
-
-static mut WINDOW_CLASS: *const Class = ptr::null();
-static mut PANEL_CLASS: *const Class = ptr::null();
-static mut VIEW_CLASS: *const Class = ptr::null();
-
-#[allow(non_upper_case_globals)]
-const NSWindowStyleMaskNonactivatingPanel: NSWindowStyleMask =
-    unsafe { NSWindowStyleMask::from_bits_unchecked(1 << 7) };
-#[allow(non_upper_case_globals)]
-const NSNormalWindowLevel: NSInteger = 0;
-#[allow(non_upper_case_globals)]
-const NSPopUpWindowLevel: NSInteger = 101;
-#[allow(non_upper_case_globals)]
-const NSTrackingMouseEnteredAndExited: NSUInteger = 0x01;
-#[allow(non_upper_case_globals)]
-const NSTrackingMouseMoved: NSUInteger = 0x02;
-#[allow(non_upper_case_globals)]
-const NSTrackingActiveAlways: NSUInteger = 0x80;
-#[allow(non_upper_case_globals)]
-const NSTrackingInVisibleRect: NSUInteger = 0x200;
-#[allow(non_upper_case_globals)]
-const NSWindowAnimationBehaviorUtilityWindow: NSInteger = 4;
-#[allow(non_upper_case_globals)]
-const NSViewLayerContentsRedrawDuringViewResize: NSInteger = 2;
-// https://developer.apple.com/documentation/appkit/nsdragoperation
-type NSDragOperation = NSUInteger;
-#[allow(non_upper_case_globals)]
-const NSDragOperationNone: NSDragOperation = 0;
-#[allow(non_upper_case_globals)]
-const NSDragOperationCopy: NSDragOperation = 1;
-
-#[ctor]
-unsafe fn build_classes() {
-    ::util::gpui2_loaded();
-
-    WINDOW_CLASS = build_window_class("GPUI2Window", class!(NSWindow));
-    PANEL_CLASS = build_window_class("GPUI2Panel", class!(NSPanel));
-    VIEW_CLASS = {
-        let mut decl = ClassDecl::new("GPUI2View", class!(NSView)).unwrap();
-        decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR);
-
-        decl.add_method(sel!(dealloc), dealloc_view as extern "C" fn(&Object, Sel));
-
-        decl.add_method(
-            sel!(performKeyEquivalent:),
-            handle_key_equivalent as extern "C" fn(&Object, Sel, id) -> BOOL,
-        );
-        decl.add_method(
-            sel!(keyDown:),
-            handle_key_down as extern "C" fn(&Object, Sel, id),
-        );
-        decl.add_method(
-            sel!(mouseDown:),
-            handle_view_event as extern "C" fn(&Object, Sel, id),
-        );
-        decl.add_method(
-            sel!(mouseUp:),
-            handle_view_event as extern "C" fn(&Object, Sel, id),
-        );
-        decl.add_method(
-            sel!(rightMouseDown:),
-            handle_view_event as extern "C" fn(&Object, Sel, id),
-        );
-        decl.add_method(
-            sel!(rightMouseUp:),
-            handle_view_event as extern "C" fn(&Object, Sel, id),
-        );
-        decl.add_method(
-            sel!(otherMouseDown:),
-            handle_view_event as extern "C" fn(&Object, Sel, id),
-        );
-        decl.add_method(
-            sel!(otherMouseUp:),
-            handle_view_event as extern "C" fn(&Object, Sel, id),
-        );
-        decl.add_method(
-            sel!(mouseMoved:),
-            handle_view_event as extern "C" fn(&Object, Sel, id),
-        );
-        decl.add_method(
-            sel!(mouseExited:),
-            handle_view_event as extern "C" fn(&Object, Sel, id),
-        );
-        decl.add_method(
-            sel!(mouseDragged:),
-            handle_view_event as extern "C" fn(&Object, Sel, id),
-        );
-        decl.add_method(
-            sel!(scrollWheel:),
-            handle_view_event as extern "C" fn(&Object, Sel, id),
-        );
-        decl.add_method(
-            sel!(flagsChanged:),
-            handle_view_event as extern "C" fn(&Object, Sel, id),
-        );
-        decl.add_method(
-            sel!(cancelOperation:),
-            cancel_operation as extern "C" fn(&Object, Sel, id),
-        );
-
-        decl.add_method(
-            sel!(makeBackingLayer),
-            make_backing_layer as extern "C" fn(&Object, Sel) -> id,
-        );
-
-        decl.add_protocol(Protocol::get("CALayerDelegate").unwrap());
-        decl.add_method(
-            sel!(viewDidChangeBackingProperties),
-            view_did_change_backing_properties as extern "C" fn(&Object, Sel),
-        );
-        decl.add_method(
-            sel!(setFrameSize:),
-            set_frame_size as extern "C" fn(&Object, Sel, NSSize),
-        );
-        decl.add_method(
-            sel!(displayLayer:),
-            display_layer as extern "C" fn(&Object, Sel, id),
-        );
-
-        decl.add_protocol(Protocol::get("NSTextInputClient").unwrap());
-        decl.add_method(
-            sel!(validAttributesForMarkedText),
-            valid_attributes_for_marked_text as extern "C" fn(&Object, Sel) -> id,
-        );
-        decl.add_method(
-            sel!(hasMarkedText),
-            has_marked_text as extern "C" fn(&Object, Sel) -> BOOL,
-        );
-        decl.add_method(
-            sel!(markedRange),
-            marked_range as extern "C" fn(&Object, Sel) -> NSRange,
-        );
-        decl.add_method(
-            sel!(selectedRange),
-            selected_range as extern "C" fn(&Object, Sel) -> NSRange,
-        );
-        decl.add_method(
-            sel!(firstRectForCharacterRange:actualRange:),
-            first_rect_for_character_range as extern "C" fn(&Object, Sel, NSRange, id) -> NSRect,
-        );
-        decl.add_method(
-            sel!(insertText:replacementRange:),
-            insert_text as extern "C" fn(&Object, Sel, id, NSRange),
-        );
-        decl.add_method(
-            sel!(setMarkedText:selectedRange:replacementRange:),
-            set_marked_text as extern "C" fn(&Object, Sel, id, NSRange, NSRange),
-        );
-        decl.add_method(sel!(unmarkText), unmark_text as extern "C" fn(&Object, Sel));
-        decl.add_method(
-            sel!(attributedSubstringForProposedRange:actualRange:),
-            attributed_substring_for_proposed_range
-                as extern "C" fn(&Object, Sel, NSRange, *mut c_void) -> id,
-        );
-        decl.add_method(
-            sel!(viewDidChangeEffectiveAppearance),
-            view_did_change_effective_appearance as extern "C" fn(&Object, Sel),
-        );
-
-        // Suppress beep on keystrokes with modifier keys.
-        decl.add_method(
-            sel!(doCommandBySelector:),
-            do_command_by_selector as extern "C" fn(&Object, Sel, Sel),
-        );
-
-        decl.add_method(
-            sel!(acceptsFirstMouse:),
-            accepts_first_mouse as extern "C" fn(&Object, Sel, id) -> BOOL,
-        );
-
-        decl.register()
-    };
-}
-
-pub fn convert_mouse_position(position: NSPoint, window_height: Pixels) -> Point<Pixels> {
-    point(
-        px(position.x as f32),
-        // MacOS screen coordinates are relative to bottom left
-        window_height - px(position.y as f32),
-    )
-}
-
-unsafe fn build_window_class(name: &'static str, superclass: &Class) -> *const Class {
-    let mut decl = ClassDecl::new(name, superclass).unwrap();
-    decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR);
-    decl.add_method(sel!(dealloc), dealloc_window as extern "C" fn(&Object, Sel));
-    decl.add_method(
-        sel!(canBecomeMainWindow),
-        yes as extern "C" fn(&Object, Sel) -> BOOL,
-    );
-    decl.add_method(
-        sel!(canBecomeKeyWindow),
-        yes as extern "C" fn(&Object, Sel) -> BOOL,
-    );
-    decl.add_method(
-        sel!(windowDidResize:),
-        window_did_resize as extern "C" fn(&Object, Sel, id),
-    );
-    decl.add_method(
-        sel!(windowWillEnterFullScreen:),
-        window_will_enter_fullscreen as extern "C" fn(&Object, Sel, id),
-    );
-    decl.add_method(
-        sel!(windowWillExitFullScreen:),
-        window_will_exit_fullscreen as extern "C" fn(&Object, Sel, id),
-    );
-    decl.add_method(
-        sel!(windowDidMove:),
-        window_did_move as extern "C" fn(&Object, Sel, id),
-    );
-    decl.add_method(
-        sel!(windowDidBecomeKey:),
-        window_did_change_key_status as extern "C" fn(&Object, Sel, id),
-    );
-    decl.add_method(
-        sel!(windowDidResignKey:),
-        window_did_change_key_status as extern "C" fn(&Object, Sel, id),
-    );
-    decl.add_method(
-        sel!(windowShouldClose:),
-        window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL,
-    );
-    decl.add_method(sel!(close), close_window as extern "C" fn(&Object, Sel));
-
-    decl.add_method(
-        sel!(draggingEntered:),
-        dragging_entered as extern "C" fn(&Object, Sel, id) -> NSDragOperation,
-    );
-    decl.add_method(
-        sel!(draggingUpdated:),
-        dragging_updated as extern "C" fn(&Object, Sel, id) -> NSDragOperation,
-    );
-    decl.add_method(
-        sel!(draggingExited:),
-        dragging_exited as extern "C" fn(&Object, Sel, id),
-    );
-    decl.add_method(
-        sel!(performDragOperation:),
-        perform_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL,
-    );
-    decl.add_method(
-        sel!(concludeDragOperation:),
-        conclude_drag_operation as extern "C" fn(&Object, Sel, id),
-    );
-
-    decl.register()
-}
-
-///Used to track what the IME does when we send it a keystroke.
-///This is only used to handle the case where the IME mysteriously
-///swallows certain keys.
-///
-///Basically a direct copy of the approach that WezTerm uses in:
-///github.com/wez/wezterm : d5755f3e : window/src/os/macos/window.rs
-enum ImeState {
-    Continue,
-    Acted,
-    None,
-}
-
-struct InsertText {
-    replacement_range: Option<Range<usize>>,
-    text: String,
-}
-
-struct MacWindowState {
-    handle: AnyWindowHandle,
-    executor: ForegroundExecutor,
-    native_window: id,
-    renderer: MetalRenderer,
-    draw: Option<DrawWindow>,
-    kind: WindowKind,
-    event_callback: Option<Box<dyn FnMut(InputEvent) -> bool>>,
-    activate_callback: Option<Box<dyn FnMut(bool)>>,
-    resize_callback: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
-    fullscreen_callback: Option<Box<dyn FnMut(bool)>>,
-    moved_callback: Option<Box<dyn FnMut()>>,
-    should_close_callback: Option<Box<dyn FnMut() -> bool>>,
-    close_callback: Option<Box<dyn FnOnce()>>,
-    appearance_changed_callback: Option<Box<dyn FnMut()>>,
-    input_handler: Option<Box<dyn PlatformInputHandler>>,
-    pending_key_down: Option<(KeyDownEvent, Option<InsertText>)>,
-    last_key_equivalent: Option<KeyDownEvent>,
-    synthetic_drag_counter: usize,
-    last_fresh_keydown: Option<Keystroke>,
-    traffic_light_position: Option<Point<Pixels>>,
-    previous_modifiers_changed_event: Option<InputEvent>,
-    // State tracking what the IME did after the last request
-    ime_state: ImeState,
-    // Retains the last IME Text
-    ime_text: Option<String>,
-}
-
-impl MacWindowState {
-    fn move_traffic_light(&self) {
-        if let Some(traffic_light_position) = self.traffic_light_position {
-            let titlebar_height = self.titlebar_height();
-
-            unsafe {
-                let close_button: id = msg_send![
-                    self.native_window,
-                    standardWindowButton: NSWindowButton::NSWindowCloseButton
-                ];
-                let min_button: id = msg_send![
-                    self.native_window,
-                    standardWindowButton: NSWindowButton::NSWindowMiniaturizeButton
-                ];
-                let zoom_button: id = msg_send![
-                    self.native_window,
-                    standardWindowButton: NSWindowButton::NSWindowZoomButton
-                ];
-
-                let mut close_button_frame: CGRect = msg_send![close_button, frame];
-                let mut min_button_frame: CGRect = msg_send![min_button, frame];
-                let mut zoom_button_frame: CGRect = msg_send![zoom_button, frame];
-                let mut origin = point(
-                    traffic_light_position.x,
-                    titlebar_height
-                        - traffic_light_position.y
-                        - px(close_button_frame.size.height as f32),
-                );
-                let button_spacing =
-                    px((min_button_frame.origin.x - close_button_frame.origin.x) as f32);
-
-                close_button_frame.origin = CGPoint::new(origin.x.into(), origin.y.into());
-                let _: () = msg_send![close_button, setFrame: close_button_frame];
-                origin.x += button_spacing;
-
-                min_button_frame.origin = CGPoint::new(origin.x.into(), origin.y.into());
-                let _: () = msg_send![min_button, setFrame: min_button_frame];
-                origin.x += button_spacing;
-
-                zoom_button_frame.origin = CGPoint::new(origin.x.into(), origin.y.into());
-                let _: () = msg_send![zoom_button, setFrame: zoom_button_frame];
-                origin.x += button_spacing;
-            }
-        }
-    }
-
-    fn is_fullscreen(&self) -> bool {
-        unsafe {
-            let style_mask = self.native_window.styleMask();
-            style_mask.contains(NSWindowStyleMask::NSFullScreenWindowMask)
-        }
-    }
-
-    fn bounds(&self) -> WindowBounds {
-        unsafe {
-            if self.is_fullscreen() {
-                return WindowBounds::Fullscreen;
-            }
-
-            let frame = self.frame();
-            let screen_size = self.native_window.screen().visibleFrame().into();
-            if frame.size == screen_size {
-                WindowBounds::Maximized
-            } else {
-                WindowBounds::Fixed(frame)
-            }
-        }
-    }
-
-    fn frame(&self) -> Bounds<GlobalPixels> {
-        unsafe {
-            let frame = NSWindow::frame(self.native_window);
-            display_bounds_from_native(mem::transmute::<NSRect, CGRect>(frame))
-        }
-    }
-
-    fn content_size(&self) -> Size<Pixels> {
-        let NSSize { width, height, .. } =
-            unsafe { NSView::frame(self.native_window.contentView()) }.size;
-        size(px(width as f32), px(height as f32))
-    }
-
-    fn scale_factor(&self) -> f32 {
-        get_scale_factor(self.native_window)
-    }
-
-    fn titlebar_height(&self) -> Pixels {
-        unsafe {
-            let frame = NSWindow::frame(self.native_window);
-            let content_layout_rect: CGRect = msg_send![self.native_window, contentLayoutRect];
-            px((frame.size.height - content_layout_rect.size.height) as f32)
-        }
-    }
-
-    fn to_screen_ns_point(&self, point: Point<Pixels>) -> NSPoint {
-        unsafe {
-            let point = NSPoint::new(
-                point.x.into(),
-                (self.content_size().height - point.y).into(),
-            );
-            msg_send![self.native_window, convertPointToScreen: point]
-        }
-    }
-}
-
-unsafe impl Send for MacWindowState {}
-
-pub struct MacWindow(Arc<Mutex<MacWindowState>>);
-
-impl MacWindow {
-    pub fn open(
-        handle: AnyWindowHandle,
-        options: WindowOptions,
-        draw: DrawWindow,
-        executor: ForegroundExecutor,
-    ) -> Self {
-        unsafe {
-            let pool = NSAutoreleasePool::new(nil);
-
-            let mut style_mask;
-            if let Some(titlebar) = options.titlebar.as_ref() {
-                style_mask = NSWindowStyleMask::NSClosableWindowMask
-                    | NSWindowStyleMask::NSMiniaturizableWindowMask
-                    | NSWindowStyleMask::NSResizableWindowMask
-                    | NSWindowStyleMask::NSTitledWindowMask;
-
-                if titlebar.appears_transparent {
-                    style_mask |= NSWindowStyleMask::NSFullSizeContentViewWindowMask;
-                }
-            } else {
-                style_mask = NSWindowStyleMask::NSTitledWindowMask
-                    | NSWindowStyleMask::NSFullSizeContentViewWindowMask;
-            }
-
-            let native_window: id = match options.kind {
-                WindowKind::Normal => msg_send![WINDOW_CLASS, alloc],
-                WindowKind::PopUp => {
-                    style_mask |= NSWindowStyleMaskNonactivatingPanel;
-                    msg_send![PANEL_CLASS, alloc]
-                }
-            };
-
-            let display = options
-                .display_id
-                .and_then(|display_id| MacDisplay::all().find(|display| display.id() == display_id))
-                .unwrap_or_else(MacDisplay::primary);
-
-            let mut target_screen = nil;
-            let screens = NSScreen::screens(nil);
-            let count: u64 = cocoa::foundation::NSArray::count(screens);
-            for i in 0..count {
-                let screen = cocoa::foundation::NSArray::objectAtIndex(screens, i);
-                let device_description = NSScreen::deviceDescription(screen);
-                let screen_number_key: id = NSString::alloc(nil).init_str("NSScreenNumber");
-                let screen_number = device_description.objectForKey_(screen_number_key);
-                let screen_number: NSUInteger = msg_send![screen_number, unsignedIntegerValue];
-                if screen_number as u32 == display.id().0 {
-                    target_screen = screen;
-                    break;
-                }
-            }
-
-            let native_window = native_window.initWithContentRect_styleMask_backing_defer_screen_(
-                NSRect::new(NSPoint::new(0., 0.), NSSize::new(1024., 768.)),
-                style_mask,
-                NSBackingStoreBuffered,
-                NO,
-                target_screen,
-            );
-            assert!(!native_window.is_null());
-            let () = msg_send![
-                native_window,
-                registerForDraggedTypes:
-                    NSArray::arrayWithObject(nil, NSFilenamesPboardType)
-            ];
-
-            let screen = native_window.screen();
-            match options.bounds {
-                WindowBounds::Fullscreen => {
-                    native_window.toggleFullScreen_(nil);
-                }
-                WindowBounds::Maximized => {
-                    native_window.setFrame_display_(screen.visibleFrame(), YES);
-                }
-                WindowBounds::Fixed(bounds) => {
-                    let display_bounds = display.bounds();
-                    let frame = if bounds.intersects(&display_bounds) {
-                        display_bounds_to_native(bounds)
-                    } else {
-                        display_bounds_to_native(display_bounds)
-                    };
-                    native_window.setFrame_display_(mem::transmute::<CGRect, NSRect>(frame), YES);
-                }
-            }
-
-            let native_view: id = msg_send![VIEW_CLASS, alloc];
-            let native_view = NSView::init(native_view);
-
-            assert!(!native_view.is_null());
-
-            let window = Self(Arc::new(Mutex::new(MacWindowState {
-                handle,
-                executor,
-                native_window,
-                renderer: MetalRenderer::new(true),
-                draw: Some(draw),
-                kind: options.kind,
-                event_callback: None,
-                activate_callback: None,
-                resize_callback: None,
-                fullscreen_callback: None,
-                moved_callback: None,
-                should_close_callback: None,
-                close_callback: None,
-                appearance_changed_callback: None,
-                input_handler: None,
-                pending_key_down: None,
-                last_key_equivalent: None,
-                synthetic_drag_counter: 0,
-                last_fresh_keydown: None,
-                traffic_light_position: options
-                    .titlebar
-                    .as_ref()
-                    .and_then(|titlebar| titlebar.traffic_light_position),
-                previous_modifiers_changed_event: None,
-                ime_state: ImeState::None,
-                ime_text: None,
-            })));
-
-            (*native_window).set_ivar(
-                WINDOW_STATE_IVAR,
-                Arc::into_raw(window.0.clone()) as *const c_void,
-            );
-            native_window.setDelegate_(native_window);
-            (*native_view).set_ivar(
-                WINDOW_STATE_IVAR,
-                Arc::into_raw(window.0.clone()) as *const c_void,
-            );
-
-            if let Some(title) = options
-                .titlebar
-                .as_ref()
-                .and_then(|t| t.title.as_ref().map(AsRef::as_ref))
-            {
-                native_window.setTitle_(NSString::alloc(nil).init_str(title));
-            }
-
-            native_window.setMovable_(options.is_movable as BOOL);
-
-            if options
-                .titlebar
-                .map_or(true, |titlebar| titlebar.appears_transparent)
-            {
-                native_window.setTitlebarAppearsTransparent_(YES);
-                native_window.setTitleVisibility_(NSWindowTitleVisibility::NSWindowTitleHidden);
-            }
-
-            native_view.setAutoresizingMask_(NSViewWidthSizable | NSViewHeightSizable);
-            native_view.setWantsBestResolutionOpenGLSurface_(YES);
-
-            // From winit crate: On Mojave, views automatically become layer-backed shortly after
-            // being added to a native_window. Changing the layer-backedness of a view breaks the
-            // association between the view and its associated OpenGL context. To work around this,
-            // on we explicitly make the view layer-backed up front so that AppKit doesn't do it
-            // itself and break the association with its context.
-            native_view.setWantsLayer(YES);
-            let _: () = msg_send![
-                native_view,
-                setLayerContentsRedrawPolicy: NSViewLayerContentsRedrawDuringViewResize
-            ];
-
-            native_window.setContentView_(native_view.autorelease());
-            native_window.makeFirstResponder_(native_view);
-
-            if options.center {
-                native_window.center();
-            }
-
-            match options.kind {
-                WindowKind::Normal => {
-                    native_window.setLevel_(NSNormalWindowLevel);
-                    native_window.setAcceptsMouseMovedEvents_(YES);
-                }
-                WindowKind::PopUp => {
-                    // Use a tracking area to allow receiving MouseMoved events even when
-                    // the window or application aren't active, which is often the case
-                    // e.g. for notification windows.
-                    let tracking_area: id = msg_send![class!(NSTrackingArea), alloc];
-                    let _: () = msg_send![
-                        tracking_area,
-                        initWithRect: NSRect::new(NSPoint::new(0., 0.), NSSize::new(0., 0.))
-                        options: NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveAlways | NSTrackingInVisibleRect
-                        owner: native_view
-                        userInfo: nil
-                    ];
-                    let _: () =
-                        msg_send![native_view, addTrackingArea: tracking_area.autorelease()];
-
-                    native_window.setLevel_(NSPopUpWindowLevel);
-                    let _: () = msg_send![
-                        native_window,
-                        setAnimationBehavior: NSWindowAnimationBehaviorUtilityWindow
-                    ];
-                    native_window.setCollectionBehavior_(
-                        NSWindowCollectionBehavior::NSWindowCollectionBehaviorCanJoinAllSpaces |
-                        NSWindowCollectionBehavior::NSWindowCollectionBehaviorFullScreenAuxiliary
-                    );
-                }
-            }
-            if options.focus {
-                native_window.makeKeyAndOrderFront_(nil);
-            } else if options.show {
-                native_window.orderFront_(nil);
-            }
-
-            window.0.lock().move_traffic_light();
-            pool.drain();
-
-            window
-        }
-    }
-
-    pub fn active_window() -> Option<AnyWindowHandle> {
-        unsafe {
-            let app = NSApplication::sharedApplication(nil);
-            let main_window: id = msg_send![app, mainWindow];
-            if msg_send![main_window, isKindOfClass: WINDOW_CLASS] {
-                let handle = get_window_state(&*main_window).lock().handle;
-                Some(handle)
-            } else {
-                None
-            }
-        }
-    }
-}
-
-impl Drop for MacWindow {
-    fn drop(&mut self) {
-        let this = self.0.lock();
-        let window = this.native_window;
-        this.executor
-            .spawn(async move {
-                unsafe {
-                    // todo!() this panic()s when you click the red close button
-                    // unless should_close returns false.
-                    // (luckliy in zed it always returns false)
-                    window.close();
-                }
-            })
-            .detach();
-    }
-}
-
-impl PlatformWindow for MacWindow {
-    fn bounds(&self) -> WindowBounds {
-        self.0.as_ref().lock().bounds()
-    }
-
-    fn content_size(&self) -> Size<Pixels> {
-        self.0.as_ref().lock().content_size()
-    }
-
-    fn scale_factor(&self) -> f32 {
-        self.0.as_ref().lock().scale_factor()
-    }
-
-    fn titlebar_height(&self) -> Pixels {
-        self.0.as_ref().lock().titlebar_height()
-    }
-
-    fn appearance(&self) -> WindowAppearance {
-        unsafe {
-            let appearance: id = msg_send![self.0.lock().native_window, effectiveAppearance];
-            WindowAppearance::from_native(appearance)
-        }
-    }
-
-    fn display(&self) -> Rc<dyn PlatformDisplay> {
-        unsafe {
-            let screen = self.0.lock().native_window.screen();
-            let device_description: id = msg_send![screen, deviceDescription];
-            let screen_number: id = NSDictionary::valueForKey_(
-                device_description,
-                NSString::alloc(nil).init_str("NSScreenNumber"),
-            );
-
-            let screen_number: u32 = msg_send![screen_number, unsignedIntValue];
-
-            Rc::new(MacDisplay(screen_number))
-        }
-    }
-
-    fn mouse_position(&self) -> Point<Pixels> {
-        let position = unsafe {
-            self.0
-                .lock()
-                .native_window
-                .mouseLocationOutsideOfEventStream()
-        };
-        convert_mouse_position(position, self.content_size().height)
-    }
-
-    fn modifiers(&self) -> Modifiers {
-        unsafe {
-            let modifiers: NSEventModifierFlags = msg_send![class!(NSEvent), modifierFlags];
-
-            let control = modifiers.contains(NSEventModifierFlags::NSControlKeyMask);
-            let alt = modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask);
-            let shift = modifiers.contains(NSEventModifierFlags::NSShiftKeyMask);
-            let command = modifiers.contains(NSEventModifierFlags::NSCommandKeyMask);
-            let function = modifiers.contains(NSEventModifierFlags::NSFunctionKeyMask);
-
-            Modifiers {
-                control,
-                alt,
-                shift,
-                command,
-                function,
-            }
-        }
-    }
-
-    fn as_any_mut(&mut self) -> &mut dyn Any {
-        self
-    }
-
-    fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>) {
-        self.0.as_ref().lock().input_handler = Some(input_handler);
-    }
-
-    fn clear_input_handler(&mut self) {
-        self.0.as_ref().lock().input_handler = None;
-    }
-
-    fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver<usize> {
-        // macOs applies overrides to modal window buttons after they are added.
-        // Two most important for this logic are:
-        // * Buttons with "Cancel" title will be displayed as the last buttons in the modal
-        // * Last button added to the modal via `addButtonWithTitle` stays focused
-        // * Focused buttons react on "space"/" " keypresses
-        // * Usage of `keyEquivalent`, `makeFirstResponder` or `setInitialFirstResponder` does not change the focus
-        //
-        // See also https://developer.apple.com/documentation/appkit/nsalert/1524532-addbuttonwithtitle#discussion
-        // ```
-        // By default, the first button has a key equivalent of Return,
-        // any button with a title of “Cancel” has a key equivalent of Escape,
-        // and any button with the title “Don’t Save” has a key equivalent of Command-D (but only if it’s not the first button).
-        // ```
-        //
-        // To avoid situations when the last element added is "Cancel" and it gets the focus
-        // (hence stealing both ESC and Space shortcuts), we find and add one non-Cancel button
-        // last, so it gets focus and a Space shortcut.
-        // This way, "Save this file? Yes/No/Cancel"-ish modals will get all three buttons mapped with a key.
-        let latest_non_cancel_label = answers
-            .iter()
-            .enumerate()
-            .rev()
-            .find(|(_, &label)| label != "Cancel")
-            .filter(|&(label_index, _)| label_index > 0);
-
-        unsafe {
-            let alert: id = msg_send![class!(NSAlert), alloc];
-            let alert: id = msg_send![alert, init];
-            let alert_style = match level {
-                PromptLevel::Info => 1,
-                PromptLevel::Warning => 0,
-                PromptLevel::Critical => 2,
-            };
-            let _: () = msg_send![alert, setAlertStyle: alert_style];
-            let _: () = msg_send![alert, setMessageText: ns_string(msg)];
-
-            for (ix, answer) in answers
-                .iter()
-                .enumerate()
-                .filter(|&(ix, _)| Some(ix) != latest_non_cancel_label.map(|(ix, _)| ix))
-            {
-                let button: id = msg_send![alert, addButtonWithTitle: ns_string(answer)];
-                let _: () = msg_send![button, setTag: ix as NSInteger];
-            }
-            if let Some((ix, answer)) = latest_non_cancel_label {
-                let button: id = msg_send![alert, addButtonWithTitle: ns_string(answer)];
-                let _: () = msg_send![button, setTag: ix as NSInteger];
-            }
-
-            let (done_tx, done_rx) = oneshot::channel();
-            let done_tx = Cell::new(Some(done_tx));
-            let block = ConcreteBlock::new(move |answer: NSInteger| {
-                if let Some(done_tx) = done_tx.take() {
-                    let _ = done_tx.send(answer.try_into().unwrap());
-                }
-            });
-            let block = block.copy();
-            let native_window = self.0.lock().native_window;
-            let executor = self.0.lock().executor.clone();
-            executor
-                .spawn(async move {
-                    let _: () = msg_send![
-                        alert,
-                        beginSheetModalForWindow: native_window
-                        completionHandler: block
-                    ];
-                })
-                .detach();
-
-            done_rx
-        }
-    }
-
-    fn activate(&self) {
-        let window = self.0.lock().native_window;
-        let executor = self.0.lock().executor.clone();
-        executor
-            .spawn(async move {
-                unsafe {
-                    let _: () = msg_send![window, makeKeyAndOrderFront: nil];
-                }
-            })
-            .detach();
-    }
-
-    fn set_title(&mut self, title: &str) {
-        unsafe {
-            let app = NSApplication::sharedApplication(nil);
-            let window = self.0.lock().native_window;
-            let title = ns_string(title);
-            let _: () = msg_send![app, changeWindowsItem:window title:title filename:false];
-            let _: () = msg_send![window, setTitle: title];
-            self.0.lock().move_traffic_light();
-        }
-    }
-
-    fn set_edited(&mut self, edited: bool) {
-        unsafe {
-            let window = self.0.lock().native_window;
-            msg_send![window, setDocumentEdited: edited as BOOL]
-        }
-
-        // Changing the document edited state resets the traffic light position,
-        // so we have to move it again.
-        self.0.lock().move_traffic_light();
-    }
-
-    fn show_character_palette(&self) {
-        let this = self.0.lock();
-        let window = this.native_window;
-        this.executor
-            .spawn(async move {
-                unsafe {
-                    let app = NSApplication::sharedApplication(nil);
-                    let _: () = msg_send![app, orderFrontCharacterPalette: window];
-                }
-            })
-            .detach();
-    }
-
-    fn minimize(&self) {
-        let window = self.0.lock().native_window;
-        unsafe {
-            window.miniaturize_(nil);
-        }
-    }
-
-    fn zoom(&self) {
-        let this = self.0.lock();
-        let window = this.native_window;
-        this.executor
-            .spawn(async move {
-                unsafe {
-                    window.zoom_(nil);
-                }
-            })
-            .detach();
-    }
-
-    fn toggle_full_screen(&self) {
-        let this = self.0.lock();
-        let window = this.native_window;
-        this.executor
-            .spawn(async move {
-                unsafe {
-                    window.toggleFullScreen_(nil);
-                }
-            })
-            .detach();
-    }
-
-    fn on_input(&self, callback: Box<dyn FnMut(InputEvent) -> bool>) {
-        self.0.as_ref().lock().event_callback = Some(callback);
-    }
-
-    fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
-        self.0.as_ref().lock().activate_callback = Some(callback);
-    }
-
-    fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
-        self.0.as_ref().lock().resize_callback = Some(callback);
-    }
-
-    fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>) {
-        self.0.as_ref().lock().fullscreen_callback = Some(callback);
-    }
-
-    fn on_moved(&self, callback: Box<dyn FnMut()>) {
-        self.0.as_ref().lock().moved_callback = Some(callback);
-    }
-
-    fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
-        self.0.as_ref().lock().should_close_callback = Some(callback);
-    }
-
-    fn on_close(&self, callback: Box<dyn FnOnce()>) {
-        self.0.as_ref().lock().close_callback = Some(callback);
-    }
-
-    fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
-        self.0.lock().appearance_changed_callback = Some(callback);
-    }
-
-    fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool {
-        let self_borrow = self.0.lock();
-        let self_handle = self_borrow.handle;
-
-        unsafe {
-            let app = NSApplication::sharedApplication(nil);
-
-            // Convert back to screen coordinates
-            let screen_point = self_borrow.to_screen_ns_point(position);
-
-            let window_number: NSInteger = msg_send![class!(NSWindow), windowNumberAtPoint:screen_point belowWindowWithWindowNumber:0];
-            let top_most_window: id = msg_send![app, windowWithWindowNumber: window_number];
-
-            let is_panel: BOOL = msg_send![top_most_window, isKindOfClass: PANEL_CLASS];
-            let is_window: BOOL = msg_send![top_most_window, isKindOfClass: WINDOW_CLASS];
-            if is_panel == YES || is_window == YES {
-                let topmost_window = get_window_state(&*top_most_window).lock().handle;
-                topmost_window == self_handle
-            } else {
-                // Someone else's window is on top
-                false
-            }
-        }
-    }
-
-    fn invalidate(&self) {
-        let this = self.0.lock();
-        unsafe {
-            let _: () = msg_send![this.native_window.contentView(), setNeedsDisplay: YES];
-        }
-    }
-
-    fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
-        self.0.lock().renderer.sprite_atlas().clone()
-    }
-}
-
-fn get_scale_factor(native_window: id) -> f32 {
-    unsafe {
-        let screen: id = msg_send![native_window, screen];
-        NSScreen::backingScaleFactor(screen) as f32
-    }
-}
-
-unsafe fn get_window_state(object: &Object) -> Arc<Mutex<MacWindowState>> {
-    let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
-    let rc1 = Arc::from_raw(raw as *mut Mutex<MacWindowState>);
-    let rc2 = rc1.clone();
-    mem::forget(rc1);
-    rc2
-}
-
-unsafe fn drop_window_state(object: &Object) {
-    let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
-    Rc::from_raw(raw as *mut RefCell<MacWindowState>);
-}
-
-extern "C" fn yes(_: &Object, _: Sel) -> BOOL {
-    YES
-}
-
-extern "C" fn dealloc_window(this: &Object, _: Sel) {
-    unsafe {
-        drop_window_state(this);
-        let _: () = msg_send![super(this, class!(NSWindow)), dealloc];
-    }
-}
-
-extern "C" fn dealloc_view(this: &Object, _: Sel) {
-    unsafe {
-        drop_window_state(this);
-        let _: () = msg_send![super(this, class!(NSView)), dealloc];
-    }
-}
-
-extern "C" fn handle_key_equivalent(this: &Object, _: Sel, native_event: id) -> BOOL {
-    handle_key_event(this, native_event, true)
-}
-
-extern "C" fn handle_key_down(this: &Object, _: Sel, native_event: id) {
-    handle_key_event(this, native_event, false);
-}
-
-extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent: bool) -> BOOL {
-    let window_state = unsafe { get_window_state(this) };
-    let mut lock = window_state.as_ref().lock();
-
-    let window_height = lock.content_size().height;
-    let event = unsafe { InputEvent::from_native(native_event, Some(window_height)) };
-
-    if let Some(InputEvent::KeyDown(event)) = event {
-        // For certain keystrokes, macOS will first dispatch a "key equivalent" event.
-        // If that event isn't handled, it will then dispatch a "key down" event. GPUI
-        // makes no distinction between these two types of events, so we need to ignore
-        // the "key down" event if we've already just processed its "key equivalent" version.
-        if key_equivalent {
-            lock.last_key_equivalent = Some(event.clone());
-        } else if lock.last_key_equivalent.take().as_ref() == Some(&event) {
-            return NO;
-        }
-
-        let keydown = event.keystroke.clone();
-        let fn_modifier = keydown.modifiers.function;
-        // Ignore events from held-down keys after some of the initially-pressed keys
-        // were released.
-        if event.is_held {
-            if lock.last_fresh_keydown.as_ref() != Some(&keydown) {
-                return YES;
-            }
-        } else {
-            lock.last_fresh_keydown = Some(keydown);
-        }
-        lock.pending_key_down = Some((event, None));
-        drop(lock);
-
-        // Send the event to the input context for IME handling, unless the `fn` modifier is
-        // being pressed.
-        if !fn_modifier {
-            unsafe {
-                let input_context: id = msg_send![this, inputContext];
-                let _: BOOL = msg_send![input_context, handleEvent: native_event];
-            }
-        }
-
-        let mut handled = false;
-        let mut lock = window_state.lock();
-        let ime_text = lock.ime_text.clone();
-        if let Some((event, insert_text)) = lock.pending_key_down.take() {
-            let is_held = event.is_held;
-            if let Some(mut callback) = lock.event_callback.take() {
-                drop(lock);
-
-                let is_composing =
-                    with_input_handler(this, |input_handler| input_handler.marked_text_range())
-                        .flatten()
-                        .is_some();
-                if !is_composing {
-                    // if the IME has changed the key, we'll first emit an event with the character
-                    // generated by the IME system; then fallback to the keystroke if that is not
-                    // handled.
-                    // cases that we have working:
-                    // - " on a brazillian layout by typing <quote><space>
-                    // - ctrl-` on a brazillian layout by typing <ctrl-`>
-                    // - $ on a czech QWERTY layout by typing <alt-4>
-                    // - 4 on a czech QWERTY layout by typing <shift-4>
-                    // - ctrl-4 on a czech QWERTY layout by typing <ctrl-alt-4> (or <ctrl-shift-4>)
-                    if ime_text.is_some() && ime_text.as_ref() != Some(&event.keystroke.key) {
-                        let event_with_ime_text = KeyDownEvent {
-                            is_held: false,
-                            keystroke: Keystroke {
-                                // we match ctrl because some use-cases need it.
-                                // we don't match alt because it's often used to generate the optional character
-                                // we don't match shift because we're not here with letters (usually)
-                                // we don't match cmd/fn because they don't seem to use IME
-                                modifiers: Default::default(),
-                                key: ime_text.clone().unwrap(),
-                                ime_key: None, // todo!("handle IME key")
-                            },
-                        };
-                        handled = callback(InputEvent::KeyDown(event_with_ime_text));
-                    }
-                    if !handled {
-                        // empty key happens when you type a deadkey in input composition.
-                        // (e.g. on a brazillian keyboard typing quote is a deadkey)
-                        if !event.keystroke.key.is_empty() {
-                            handled = callback(InputEvent::KeyDown(event));
-                        }
-                    }
-                }
-
-                if !handled {
-                    if let Some(insert) = insert_text {
-                        handled = true;
-                        with_input_handler(this, |input_handler| {
-                            input_handler
-                                .replace_text_in_range(insert.replacement_range, &insert.text)
-                        });
-                    } else if !is_composing && is_held {
-                        if let Some(last_insert_text) = ime_text {
-                            //MacOS IME is a bit funky, and even when you've told it there's nothing to
-                            //inter it will still swallow certain keys (e.g. 'f', 'j') and not others
-                            //(e.g. 'n'). This is a problem for certain kinds of views, like the terminal
-                            with_input_handler(this, |input_handler| {
-                                if input_handler.selected_text_range().is_none() {
-                                    handled = true;
-                                    input_handler.replace_text_in_range(None, &last_insert_text)
-                                }
-                            });
-                        }
-                    }
-                }
-
-                window_state.lock().event_callback = Some(callback);
-            }
-        } else {
-            handled = true;
-        }
-
-        handled as BOOL
-    } else {
-        NO
-    }
-}
-
-extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
-    let window_state = unsafe { get_window_state(this) };
-    let weak_window_state = Arc::downgrade(&window_state);
-    let mut lock = window_state.as_ref().lock();
-    let is_active = unsafe { lock.native_window.isKeyWindow() == YES };
-
-    let window_height = lock.content_size().height;
-    let event = unsafe { InputEvent::from_native(native_event, Some(window_height)) };
-
-    if let Some(mut event) = event {
-        match &mut event {
-            InputEvent::MouseDown(
-                event @ MouseDownEvent {
-                    button: MouseButton::Left,
-                    modifiers: Modifiers { control: true, .. },
-                    ..
-                },
-            ) => {
-                // On mac, a ctrl-left click should be handled as a right click.
-                *event = MouseDownEvent {
-                    button: MouseButton::Right,
-                    modifiers: Modifiers {
-                        control: false,
-                        ..event.modifiers
-                    },
-                    click_count: 1,
-                    ..*event
-                };
-            }
-
-            // Because we map a ctrl-left_down to a right_down -> right_up let's ignore
-            // the ctrl-left_up to avoid having a mismatch in button down/up events if the
-            // user is still holding ctrl when releasing the left mouse button
-            InputEvent::MouseUp(
-                event @ MouseUpEvent {
-                    button: MouseButton::Left,
-                    modifiers: Modifiers { control: true, .. },
-                    ..
-                },
-            ) => {
-                *event = MouseUpEvent {
-                    button: MouseButton::Right,
-                    modifiers: Modifiers {
-                        control: false,
-                        ..event.modifiers
-                    },
-                    click_count: 1,
-                    ..*event
-                };
-            }
-
-            _ => {}
-        };
-
-        match &event {
-            InputEvent::MouseMove(
-                event @ MouseMoveEvent {
-                    pressed_button: Some(_),
-                    ..
-                },
-            ) => {
-                lock.synthetic_drag_counter += 1;
-                let executor = lock.executor.clone();
-                executor
-                    .spawn(synthetic_drag(
-                        weak_window_state,
-                        lock.synthetic_drag_counter,
-                        event.clone(),
-                    ))
-                    .detach();
-            }
-
-            InputEvent::MouseMove(_) if !(is_active || lock.kind == WindowKind::PopUp) => return,
-
-            InputEvent::MouseUp(MouseUpEvent { .. }) => {
-                lock.synthetic_drag_counter += 1;
-            }
-
-            InputEvent::ModifiersChanged(ModifiersChangedEvent { modifiers }) => {
-                // Only raise modifiers changed event when they have actually changed
-                if let Some(InputEvent::ModifiersChanged(ModifiersChangedEvent {
-                    modifiers: prev_modifiers,
-                })) = &lock.previous_modifiers_changed_event
-                {
-                    if prev_modifiers == modifiers {
-                        return;
-                    }
-                }
-
-                lock.previous_modifiers_changed_event = Some(event.clone());
-            }
-
-            _ => {}
-        }
-
-        if let Some(mut callback) = lock.event_callback.take() {
-            drop(lock);
-            callback(event);
-            window_state.lock().event_callback = Some(callback);
-        }
-    }
-}
-
-// Allows us to receive `cmd-.` (the shortcut for closing a dialog)
-// https://bugs.eclipse.org/bugs/show_bug.cgi?id=300620#c6
-extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
-    let window_state = unsafe { get_window_state(this) };
-    let mut lock = window_state.as_ref().lock();
-
-    let keystroke = Keystroke {
-        modifiers: Default::default(),
-        key: ".".into(),
-        ime_key: None,
-    };
-    let event = InputEvent::KeyDown(KeyDownEvent {
-        keystroke: keystroke.clone(),
-        is_held: false,
-    });
-
-    lock.last_fresh_keydown = Some(keystroke);
-    if let Some(mut callback) = lock.event_callback.take() {
-        drop(lock);
-        callback(event);
-        window_state.lock().event_callback = Some(callback);
-    }
-}
-
-extern "C" fn window_did_resize(this: &Object, _: Sel, _: id) {
-    let window_state = unsafe { get_window_state(this) };
-    window_state.as_ref().lock().move_traffic_light();
-}
-
-extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) {
-    window_fullscreen_changed(this, true);
-}
-
-extern "C" fn window_will_exit_fullscreen(this: &Object, _: Sel, _: id) {
-    window_fullscreen_changed(this, false);
-}
-
-fn window_fullscreen_changed(this: &Object, is_fullscreen: bool) {
-    let window_state = unsafe { get_window_state(this) };
-    let mut lock = window_state.as_ref().lock();
-    if let Some(mut callback) = lock.fullscreen_callback.take() {
-        drop(lock);
-        callback(is_fullscreen);
-        window_state.lock().fullscreen_callback = Some(callback);
-    }
-}
-
-extern "C" fn window_did_move(this: &Object, _: Sel, _: id) {
-    let window_state = unsafe { get_window_state(this) };
-    let mut lock = window_state.as_ref().lock();
-    if let Some(mut callback) = lock.moved_callback.take() {
-        drop(lock);
-        callback();
-        window_state.lock().moved_callback = Some(callback);
-    }
-}
-
-extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id) {
-    let window_state = unsafe { get_window_state(this) };
-    let lock = window_state.lock();
-    let is_active = unsafe { lock.native_window.isKeyWindow() == YES };
-
-    // When opening a pop-up while the application isn't active, Cocoa sends a spurious
-    // `windowDidBecomeKey` message to the previous key window even though that window
-    // isn't actually key. This causes a bug if the application is later activated while
-    // the pop-up is still open, making it impossible to activate the previous key window
-    // even if the pop-up gets closed. The only way to activate it again is to de-activate
-    // the app and re-activate it, which is a pretty bad UX.
-    // The following code detects the spurious event and invokes `resignKeyWindow`:
-    // in theory, we're not supposed to invoke this method manually but it balances out
-    // the spurious `becomeKeyWindow` event and helps us work around that bug.
-    if selector == sel!(windowDidBecomeKey:) && !is_active {
-        unsafe {
-            let _: () = msg_send![lock.native_window, resignKeyWindow];
-            return;
-        }
-    }
-
-    let executor = lock.executor.clone();
-    drop(lock);
-    executor
-        .spawn(async move {
-            let mut lock = window_state.as_ref().lock();
-            if let Some(mut callback) = lock.activate_callback.take() {
-                drop(lock);
-                callback(is_active);
-                window_state.lock().activate_callback = Some(callback);
-            };
-        })
-        .detach();
-}
-
-extern "C" fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {
-    let window_state = unsafe { get_window_state(this) };
-    let mut lock = window_state.as_ref().lock();
-    if let Some(mut callback) = lock.should_close_callback.take() {
-        drop(lock);
-        let should_close = callback();
-        window_state.lock().should_close_callback = Some(callback);
-        should_close as BOOL
-    } else {
-        YES
-    }
-}
-
-extern "C" fn close_window(this: &Object, _: Sel) {
-    unsafe {
-        let close_callback = {
-            let window_state = get_window_state(this);
-            window_state
-                .as_ref()
-                .try_lock()
-                .and_then(|mut window_state| window_state.close_callback.take())
-        };
-
-        if let Some(callback) = close_callback {
-            callback();
-        }
-
-        let _: () = msg_send![super(this, class!(NSWindow)), close];
-    }
-}
-
-extern "C" fn make_backing_layer(this: &Object, _: Sel) -> id {
-    let window_state = unsafe { get_window_state(this) };
-    let window_state = window_state.as_ref().lock();
-    window_state.renderer.layer().as_ptr() as id
-}
-
-extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel) {
-    let window_state = unsafe { get_window_state(this) };
-    let mut lock = window_state.as_ref().lock();
-
-    unsafe {
-        let scale_factor = lock.scale_factor() as f64;
-        let size = lock.content_size();
-        let drawable_size: NSSize = NSSize {
-            width: f64::from(size.width) * scale_factor,
-            height: f64::from(size.height) * scale_factor,
-        };
-
-        let _: () = msg_send![
-            lock.renderer.layer(),
-            setContentsScale: scale_factor
-        ];
-        let _: () = msg_send![
-            lock.renderer.layer(),
-            setDrawableSize: drawable_size
-        ];
-    }
-
-    if let Some(mut callback) = lock.resize_callback.take() {
-        let content_size = lock.content_size();
-        let scale_factor = lock.scale_factor();
-        drop(lock);
-        callback(content_size, scale_factor);
-        window_state.as_ref().lock().resize_callback = Some(callback);
-    };
-}
-
-extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
-    let window_state = unsafe { get_window_state(this) };
-    let lock = window_state.as_ref().lock();
-
-    if lock.content_size() == size.into() {
-        return;
-    }
-
-    unsafe {
-        let _: () = msg_send![super(this, class!(NSView)), setFrameSize: size];
-    }
-
-    let scale_factor = lock.scale_factor() as f64;
-    let drawable_size: NSSize = NSSize {
-        width: size.width * scale_factor,
-        height: size.height * scale_factor,
-    };
-
-    unsafe {
-        let _: () = msg_send![
-            lock.renderer.layer(),
-            setDrawableSize: drawable_size
-        ];
-    }
-
-    drop(lock);
-    let mut lock = window_state.lock();
-    if let Some(mut callback) = lock.resize_callback.take() {
-        let content_size = lock.content_size();
-        let scale_factor = lock.scale_factor();
-        drop(lock);
-        callback(content_size, scale_factor);
-        window_state.lock().resize_callback = Some(callback);
-    };
-}
-
-extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
-    unsafe {
-        let window_state = get_window_state(this);
-        let mut draw = window_state.lock().draw.take().unwrap();
-        let scene = draw().log_err();
-        let mut window_state = window_state.lock();
-        window_state.draw = Some(draw);
-        if let Some(scene) = scene {
-            window_state.renderer.draw(&scene);
-        }
-    }
-}
-
-extern "C" fn valid_attributes_for_marked_text(_: &Object, _: Sel) -> id {
-    unsafe { msg_send![class!(NSArray), array] }
-}
-
-extern "C" fn has_marked_text(this: &Object, _: Sel) -> BOOL {
-    with_input_handler(this, |input_handler| input_handler.marked_text_range())
-        .flatten()
-        .is_some() as BOOL
-}
-
-extern "C" fn marked_range(this: &Object, _: Sel) -> NSRange {
-    with_input_handler(this, |input_handler| input_handler.marked_text_range())
-        .flatten()
-        .map_or(NSRange::invalid(), |range| range.into())
-}
-
-extern "C" fn selected_range(this: &Object, _: Sel) -> NSRange {
-    with_input_handler(this, |input_handler| input_handler.selected_text_range())
-        .flatten()
-        .map_or(NSRange::invalid(), |range| range.into())
-}
-
-extern "C" fn first_rect_for_character_range(
-    this: &Object,
-    _: Sel,
-    range: NSRange,
-    _: id,
-) -> NSRect {
-    let frame = unsafe {
-        let window = get_window_state(this).lock().native_window;
-        NSView::frame(window)
-    };
-    with_input_handler(this, |input_handler| {
-        input_handler.bounds_for_range(range.to_range()?)
-    })
-    .flatten()
-    .map_or(
-        NSRect::new(NSPoint::new(0., 0.), NSSize::new(0., 0.)),
-        |bounds| {
-            NSRect::new(
-                NSPoint::new(
-                    frame.origin.x + bounds.origin.x.0 as f64,
-                    frame.origin.y + frame.size.height
-                        - bounds.origin.y.0 as f64
-                        - bounds.size.height.0 as f64,
-                ),
-                NSSize::new(bounds.size.width.0 as f64, bounds.size.height.0 as f64),
-            )
-        },
-    )
-}
-
-extern "C" fn insert_text(this: &Object, _: Sel, text: id, replacement_range: NSRange) {
-    unsafe {
-        let window_state = get_window_state(this);
-        let mut lock = window_state.lock();
-        let pending_key_down = lock.pending_key_down.take();
-        drop(lock);
-
-        let is_attributed_string: BOOL =
-            msg_send![text, isKindOfClass: [class!(NSAttributedString)]];
-        let text: id = if is_attributed_string == YES {
-            msg_send![text, string]
-        } else {
-            text
-        };
-        let text = CStr::from_ptr(text.UTF8String() as *mut c_char)
-            .to_str()
-            .unwrap();
-        let replacement_range = replacement_range.to_range();
-
-        window_state.lock().ime_text = Some(text.to_string());
-        window_state.lock().ime_state = ImeState::Acted;
-
-        let is_composing =
-            with_input_handler(this, |input_handler| input_handler.marked_text_range())
-                .flatten()
-                .is_some();
-
-        if is_composing || text.chars().count() > 1 || pending_key_down.is_none() {
-            with_input_handler(this, |input_handler| {
-                input_handler.replace_text_in_range(replacement_range, text)
-            });
-        } else {
-            let mut pending_key_down = pending_key_down.unwrap();
-            pending_key_down.1 = Some(InsertText {
-                replacement_range,
-                text: text.to_string(),
-            });
-            window_state.lock().pending_key_down = Some(pending_key_down);
-        }
-    }
-}
-
-extern "C" fn set_marked_text(
-    this: &Object,
-    _: Sel,
-    text: id,
-    selected_range: NSRange,
-    replacement_range: NSRange,
-) {
-    unsafe {
-        let window_state = get_window_state(this);
-        window_state.lock().pending_key_down.take();
-
-        let is_attributed_string: BOOL =
-            msg_send![text, isKindOfClass: [class!(NSAttributedString)]];
-        let text: id = if is_attributed_string == YES {
-            msg_send![text, string]
-        } else {
-            text
-        };
-        let selected_range = selected_range.to_range();
-        let replacement_range = replacement_range.to_range();
-        let text = CStr::from_ptr(text.UTF8String() as *mut c_char)
-            .to_str()
-            .unwrap();
-
-        window_state.lock().ime_state = ImeState::Acted;
-        window_state.lock().ime_text = Some(text.to_string());
-
-        with_input_handler(this, |input_handler| {
-            input_handler.replace_and_mark_text_in_range(replacement_range, text, selected_range);
-        });
-    }
-}
-
-extern "C" fn unmark_text(this: &Object, _: Sel) {
-    unsafe {
-        let state = get_window_state(this);
-        let mut borrow = state.lock();
-        borrow.ime_state = ImeState::Acted;
-        borrow.ime_text.take();
-    }
-
-    with_input_handler(this, |input_handler| input_handler.unmark_text());
-}
-
-extern "C" fn attributed_substring_for_proposed_range(
-    this: &Object,
-    _: Sel,
-    range: NSRange,
-    _actual_range: *mut c_void,
-) -> id {
-    with_input_handler(this, |input_handler| {
-        let range = range.to_range()?;
-        if range.is_empty() {
-            return None;
-        }
-
-        let selected_text = input_handler.text_for_range(range)?;
-        unsafe {
-            let string: id = msg_send![class!(NSAttributedString), alloc];
-            let string: id = msg_send![string, initWithString: ns_string(&selected_text)];
-            Some(string)
-        }
-    })
-    .flatten()
-    .unwrap_or(nil)
-}
-
-extern "C" fn do_command_by_selector(this: &Object, _: Sel, _: Sel) {
-    unsafe {
-        let state = get_window_state(this);
-        let mut borrow = state.lock();
-        borrow.ime_state = ImeState::Continue;
-        borrow.ime_text.take();
-    }
-}
-
-extern "C" fn view_did_change_effective_appearance(this: &Object, _: Sel) {
-    unsafe {
-        let state = get_window_state(this);
-        let mut lock = state.as_ref().lock();
-        if let Some(mut callback) = lock.appearance_changed_callback.take() {
-            drop(lock);
-            callback();
-            state.lock().appearance_changed_callback = Some(callback);
-        }
-    }
-}
-
-extern "C" fn accepts_first_mouse(this: &Object, _: Sel, _: id) -> BOOL {
-    unsafe {
-        let state = get_window_state(this);
-        let lock = state.as_ref().lock();
-        if lock.kind == WindowKind::PopUp {
-            YES
-        } else {
-            NO
-        }
-    }
-}
-
-extern "C" fn dragging_entered(this: &Object, _: Sel, dragging_info: id) -> NSDragOperation {
-    let window_state = unsafe { get_window_state(this) };
-    if send_new_event(&window_state, {
-        let position = drag_event_position(&window_state, dragging_info);
-        let paths = external_paths_from_event(dragging_info);
-        InputEvent::FileDrop(FileDropEvent::Entered {
-            position,
-            files: paths,
-        })
-    }) {
-        NSDragOperationCopy
-    } else {
-        NSDragOperationNone
-    }
-}
-
-extern "C" fn dragging_updated(this: &Object, _: Sel, dragging_info: id) -> NSDragOperation {
-    let window_state = unsafe { get_window_state(this) };
-    let position = drag_event_position(&window_state, dragging_info);
-    if send_new_event(
-        &window_state,
-        InputEvent::FileDrop(FileDropEvent::Pending { position }),
-    ) {
-        NSDragOperationCopy
-    } else {
-        NSDragOperationNone
-    }
-}
-
-extern "C" fn dragging_exited(this: &Object, _: Sel, _: id) {
-    let window_state = unsafe { get_window_state(this) };
-    send_new_event(&window_state, InputEvent::FileDrop(FileDropEvent::Exited));
-}
-
-extern "C" fn perform_drag_operation(this: &Object, _: Sel, dragging_info: id) -> BOOL {
-    let window_state = unsafe { get_window_state(this) };
-    let position = drag_event_position(&window_state, dragging_info);
-    if send_new_event(
-        &window_state,
-        InputEvent::FileDrop(FileDropEvent::Submit { position }),
-    ) {
-        YES
-    } else {
-        NO
-    }
-}
-
-fn external_paths_from_event(dragging_info: *mut Object) -> ExternalPaths {
-    let mut paths = SmallVec::new();
-    let pasteboard: id = unsafe { msg_send![dragging_info, draggingPasteboard] };
-    let filenames = unsafe { NSPasteboard::propertyListForType(pasteboard, NSFilenamesPboardType) };
-    for file in unsafe { filenames.iter() } {
-        let path = unsafe {
-            let f = NSString::UTF8String(file);
-            CStr::from_ptr(f).to_string_lossy().into_owned()
-        };
-        paths.push(PathBuf::from(path))
-    }
-    ExternalPaths(paths)
-}
-
-extern "C" fn conclude_drag_operation(this: &Object, _: Sel, _: id) {
-    let window_state = unsafe { get_window_state(this) };
-    send_new_event(&window_state, InputEvent::FileDrop(FileDropEvent::Exited));
-}
-
-async fn synthetic_drag(
-    window_state: Weak<Mutex<MacWindowState>>,
-    drag_id: usize,
-    event: MouseMoveEvent,
-) {
-    loop {
-        Timer::after(Duration::from_millis(16)).await;
-        if let Some(window_state) = window_state.upgrade() {
-            let mut lock = window_state.lock();
-            if lock.synthetic_drag_counter == drag_id {
-                if let Some(mut callback) = lock.event_callback.take() {
-                    drop(lock);
-                    callback(InputEvent::MouseMove(event.clone()));
-                    window_state.lock().event_callback = Some(callback);
-                }
-            } else {
-                break;
-            }
-        }
-    }
-}
-
-fn send_new_event(window_state_lock: &Mutex<MacWindowState>, e: InputEvent) -> bool {
-    let window_state = window_state_lock.lock().event_callback.take();
-    if let Some(mut callback) = window_state {
-        callback(e);
-        window_state_lock.lock().event_callback = Some(callback);
-        true
-    } else {
-        false
-    }
-}
-
-fn drag_event_position(window_state: &Mutex<MacWindowState>, dragging_info: id) -> Point<Pixels> {
-    let drag_location: NSPoint = unsafe { msg_send![dragging_info, draggingLocation] };
-    convert_mouse_position(drag_location, window_state.lock().content_size().height)
-}
-
-fn with_input_handler<F, R>(window: &Object, f: F) -> Option<R>
-where
-    F: FnOnce(&mut dyn PlatformInputHandler) -> R,
-{
-    let window_state = unsafe { get_window_state(window) };
-    let mut lock = window_state.as_ref().lock();
-    if let Some(mut input_handler) = lock.input_handler.take() {
-        drop(lock);
-        let result = f(input_handler.as_mut());
-        window_state.lock().input_handler = Some(input_handler);
-        Some(result)
-    } else {
-        None
-    }
-}

crates/gpui2/src/platform/test.rs 🔗

@@ -1,9 +0,0 @@
-mod dispatcher;
-mod display;
-mod platform;
-mod window;
-
-pub use dispatcher::*;
-pub use display::*;
-pub use platform::*;
-pub use window::*;

crates/gpui2/src/scene.rs 🔗

@@ -1,778 +0,0 @@
-use crate::{
-    point, AtlasTextureId, AtlasTile, Bounds, ContentMask, Corners, Edges, Hsla, Pixels, Point,
-    ScaledPixels, StackingOrder,
-};
-use collections::BTreeMap;
-use std::{fmt::Debug, iter::Peekable, mem, slice};
-
-// Exported to metal
-pub(crate) type PointF = Point<f32>;
-#[allow(non_camel_case_types, unused)]
-pub(crate) type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
-
-pub type LayerId = u32;
-
-pub type DrawOrder = u32;
-
-#[derive(Default)]
-pub(crate) struct SceneBuilder {
-    last_order: Option<(StackingOrder, LayerId)>,
-    layers_by_order: BTreeMap<StackingOrder, LayerId>,
-    shadows: Vec<Shadow>,
-    quads: Vec<Quad>,
-    paths: Vec<Path<ScaledPixels>>,
-    underlines: Vec<Underline>,
-    monochrome_sprites: Vec<MonochromeSprite>,
-    polychrome_sprites: Vec<PolychromeSprite>,
-    surfaces: Vec<Surface>,
-}
-
-impl SceneBuilder {
-    pub fn build(&mut self) -> Scene {
-        let mut orders = vec![0; self.layers_by_order.len()];
-        for (ix, layer_id) in self.layers_by_order.values().enumerate() {
-            orders[*layer_id as usize] = ix as u32;
-        }
-        self.layers_by_order.clear();
-        self.last_order = None;
-
-        for shadow in &mut self.shadows {
-            shadow.order = orders[shadow.order as usize];
-        }
-        self.shadows.sort_by_key(|shadow| shadow.order);
-
-        for quad in &mut self.quads {
-            quad.order = orders[quad.order as usize];
-        }
-        self.quads.sort_by_key(|quad| quad.order);
-
-        for path in &mut self.paths {
-            path.order = orders[path.order as usize];
-        }
-        self.paths.sort_by_key(|path| path.order);
-
-        for underline in &mut self.underlines {
-            underline.order = orders[underline.order as usize];
-        }
-        self.underlines.sort_by_key(|underline| underline.order);
-
-        for monochrome_sprite in &mut self.monochrome_sprites {
-            monochrome_sprite.order = orders[monochrome_sprite.order as usize];
-        }
-        self.monochrome_sprites.sort_by_key(|sprite| sprite.order);
-
-        for polychrome_sprite in &mut self.polychrome_sprites {
-            polychrome_sprite.order = orders[polychrome_sprite.order as usize];
-        }
-        self.polychrome_sprites.sort_by_key(|sprite| sprite.order);
-
-        for surface in &mut self.surfaces {
-            surface.order = orders[surface.order as usize];
-        }
-        self.surfaces.sort_by_key(|surface| surface.order);
-
-        Scene {
-            shadows: mem::take(&mut self.shadows),
-            quads: mem::take(&mut self.quads),
-            paths: mem::take(&mut self.paths),
-            underlines: mem::take(&mut self.underlines),
-            monochrome_sprites: mem::take(&mut self.monochrome_sprites),
-            polychrome_sprites: mem::take(&mut self.polychrome_sprites),
-            surfaces: mem::take(&mut self.surfaces),
-        }
-    }
-
-    pub fn insert(&mut self, order: &StackingOrder, primitive: impl Into<Primitive>) {
-        let primitive = primitive.into();
-        let clipped_bounds = primitive
-            .bounds()
-            .intersect(&primitive.content_mask().bounds);
-        if clipped_bounds.size.width <= ScaledPixels(0.)
-            || clipped_bounds.size.height <= ScaledPixels(0.)
-        {
-            return;
-        }
-
-        let layer_id = self.layer_id_for_order(order);
-        match primitive {
-            Primitive::Shadow(mut shadow) => {
-                shadow.order = layer_id;
-                self.shadows.push(shadow);
-            }
-            Primitive::Quad(mut quad) => {
-                quad.order = layer_id;
-                self.quads.push(quad);
-            }
-            Primitive::Path(mut path) => {
-                path.order = layer_id;
-                path.id = PathId(self.paths.len());
-                self.paths.push(path);
-            }
-            Primitive::Underline(mut underline) => {
-                underline.order = layer_id;
-                self.underlines.push(underline);
-            }
-            Primitive::MonochromeSprite(mut sprite) => {
-                sprite.order = layer_id;
-                self.monochrome_sprites.push(sprite);
-            }
-            Primitive::PolychromeSprite(mut sprite) => {
-                sprite.order = layer_id;
-                self.polychrome_sprites.push(sprite);
-            }
-            Primitive::Surface(mut surface) => {
-                surface.order = layer_id;
-                self.surfaces.push(surface);
-            }
-        }
-    }
-
-    fn layer_id_for_order(&mut self, order: &StackingOrder) -> u32 {
-        if let Some((last_order, last_layer_id)) = self.last_order.as_ref() {
-            if last_order == order {
-                return *last_layer_id;
-            }
-        };
-
-        let layer_id = if let Some(layer_id) = self.layers_by_order.get(order) {
-            *layer_id
-        } else {
-            let next_id = self.layers_by_order.len() as LayerId;
-            self.layers_by_order.insert(order.clone(), next_id);
-            next_id
-        };
-        self.last_order = Some((order.clone(), layer_id));
-        layer_id
-    }
-}
-
-pub struct Scene {
-    pub shadows: Vec<Shadow>,
-    pub quads: Vec<Quad>,
-    pub paths: Vec<Path<ScaledPixels>>,
-    pub underlines: Vec<Underline>,
-    pub monochrome_sprites: Vec<MonochromeSprite>,
-    pub polychrome_sprites: Vec<PolychromeSprite>,
-    pub surfaces: Vec<Surface>,
-}
-
-impl Scene {
-    pub fn paths(&self) -> &[Path<ScaledPixels>] {
-        &self.paths
-    }
-
-    pub(crate) fn batches(&self) -> impl Iterator<Item = PrimitiveBatch> {
-        BatchIterator {
-            shadows: &self.shadows,
-            shadows_start: 0,
-            shadows_iter: self.shadows.iter().peekable(),
-            quads: &self.quads,
-            quads_start: 0,
-            quads_iter: self.quads.iter().peekable(),
-            paths: &self.paths,
-            paths_start: 0,
-            paths_iter: self.paths.iter().peekable(),
-            underlines: &self.underlines,
-            underlines_start: 0,
-            underlines_iter: self.underlines.iter().peekable(),
-            monochrome_sprites: &self.monochrome_sprites,
-            monochrome_sprites_start: 0,
-            monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(),
-            polychrome_sprites: &self.polychrome_sprites,
-            polychrome_sprites_start: 0,
-            polychrome_sprites_iter: self.polychrome_sprites.iter().peekable(),
-            surfaces: &self.surfaces,
-            surfaces_start: 0,
-            surfaces_iter: self.surfaces.iter().peekable(),
-        }
-    }
-}
-
-struct BatchIterator<'a> {
-    shadows: &'a [Shadow],
-    shadows_start: usize,
-    shadows_iter: Peekable<slice::Iter<'a, Shadow>>,
-    quads: &'a [Quad],
-    quads_start: usize,
-    quads_iter: Peekable<slice::Iter<'a, Quad>>,
-    paths: &'a [Path<ScaledPixels>],
-    paths_start: usize,
-    paths_iter: Peekable<slice::Iter<'a, Path<ScaledPixels>>>,
-    underlines: &'a [Underline],
-    underlines_start: usize,
-    underlines_iter: Peekable<slice::Iter<'a, Underline>>,
-    monochrome_sprites: &'a [MonochromeSprite],
-    monochrome_sprites_start: usize,
-    monochrome_sprites_iter: Peekable<slice::Iter<'a, MonochromeSprite>>,
-    polychrome_sprites: &'a [PolychromeSprite],
-    polychrome_sprites_start: usize,
-    polychrome_sprites_iter: Peekable<slice::Iter<'a, PolychromeSprite>>,
-    surfaces: &'a [Surface],
-    surfaces_start: usize,
-    surfaces_iter: Peekable<slice::Iter<'a, Surface>>,
-}
-
-impl<'a> Iterator for BatchIterator<'a> {
-    type Item = PrimitiveBatch<'a>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        let mut orders_and_kinds = [
-            (
-                self.shadows_iter.peek().map(|s| s.order),
-                PrimitiveKind::Shadow,
-            ),
-            (self.quads_iter.peek().map(|q| q.order), PrimitiveKind::Quad),
-            (self.paths_iter.peek().map(|q| q.order), PrimitiveKind::Path),
-            (
-                self.underlines_iter.peek().map(|u| u.order),
-                PrimitiveKind::Underline,
-            ),
-            (
-                self.monochrome_sprites_iter.peek().map(|s| s.order),
-                PrimitiveKind::MonochromeSprite,
-            ),
-            (
-                self.polychrome_sprites_iter.peek().map(|s| s.order),
-                PrimitiveKind::PolychromeSprite,
-            ),
-            (
-                self.surfaces_iter.peek().map(|s| s.order),
-                PrimitiveKind::Surface,
-            ),
-        ];
-        orders_and_kinds.sort_by_key(|(order, kind)| (order.unwrap_or(u32::MAX), *kind));
-
-        let first = orders_and_kinds[0];
-        let second = orders_and_kinds[1];
-        let (batch_kind, max_order) = if first.0.is_some() {
-            (first.1, second.0.unwrap_or(u32::MAX))
-        } else {
-            return None;
-        };
-
-        match batch_kind {
-            PrimitiveKind::Shadow => {
-                let shadows_start = self.shadows_start;
-                let mut shadows_end = shadows_start + 1;
-                self.shadows_iter.next();
-                while self
-                    .shadows_iter
-                    .next_if(|shadow| shadow.order < max_order)
-                    .is_some()
-                {
-                    shadows_end += 1;
-                }
-                self.shadows_start = shadows_end;
-                Some(PrimitiveBatch::Shadows(
-                    &self.shadows[shadows_start..shadows_end],
-                ))
-            }
-            PrimitiveKind::Quad => {
-                let quads_start = self.quads_start;
-                let mut quads_end = quads_start + 1;
-                self.quads_iter.next();
-                while self
-                    .quads_iter
-                    .next_if(|quad| quad.order < max_order)
-                    .is_some()
-                {
-                    quads_end += 1;
-                }
-                self.quads_start = quads_end;
-                Some(PrimitiveBatch::Quads(&self.quads[quads_start..quads_end]))
-            }
-            PrimitiveKind::Path => {
-                let paths_start = self.paths_start;
-                let mut paths_end = paths_start + 1;
-                self.paths_iter.next();
-                while self
-                    .paths_iter
-                    .next_if(|path| path.order < max_order)
-                    .is_some()
-                {
-                    paths_end += 1;
-                }
-                self.paths_start = paths_end;
-                Some(PrimitiveBatch::Paths(&self.paths[paths_start..paths_end]))
-            }
-            PrimitiveKind::Underline => {
-                let underlines_start = self.underlines_start;
-                let mut underlines_end = underlines_start + 1;
-                self.underlines_iter.next();
-                while self
-                    .underlines_iter
-                    .next_if(|underline| underline.order < max_order)
-                    .is_some()
-                {
-                    underlines_end += 1;
-                }
-                self.underlines_start = underlines_end;
-                Some(PrimitiveBatch::Underlines(
-                    &self.underlines[underlines_start..underlines_end],
-                ))
-            }
-            PrimitiveKind::MonochromeSprite => {
-                let texture_id = self.monochrome_sprites_iter.peek().unwrap().tile.texture_id;
-                let sprites_start = self.monochrome_sprites_start;
-                let mut sprites_end = sprites_start + 1;
-                self.monochrome_sprites_iter.next();
-                while self
-                    .monochrome_sprites_iter
-                    .next_if(|sprite| {
-                        sprite.order < max_order && sprite.tile.texture_id == texture_id
-                    })
-                    .is_some()
-                {
-                    sprites_end += 1;
-                }
-                self.monochrome_sprites_start = sprites_end;
-                Some(PrimitiveBatch::MonochromeSprites {
-                    texture_id,
-                    sprites: &self.monochrome_sprites[sprites_start..sprites_end],
-                })
-            }
-            PrimitiveKind::PolychromeSprite => {
-                let texture_id = self.polychrome_sprites_iter.peek().unwrap().tile.texture_id;
-                let sprites_start = self.polychrome_sprites_start;
-                let mut sprites_end = self.polychrome_sprites_start + 1;
-                self.polychrome_sprites_iter.next();
-                while self
-                    .polychrome_sprites_iter
-                    .next_if(|sprite| {
-                        sprite.order < max_order && sprite.tile.texture_id == texture_id
-                    })
-                    .is_some()
-                {
-                    sprites_end += 1;
-                }
-                self.polychrome_sprites_start = sprites_end;
-                Some(PrimitiveBatch::PolychromeSprites {
-                    texture_id,
-                    sprites: &self.polychrome_sprites[sprites_start..sprites_end],
-                })
-            }
-            PrimitiveKind::Surface => {
-                let surfaces_start = self.surfaces_start;
-                let mut surfaces_end = surfaces_start + 1;
-                self.surfaces_iter.next();
-                while self
-                    .surfaces_iter
-                    .next_if(|surface| surface.order < max_order)
-                    .is_some()
-                {
-                    surfaces_end += 1;
-                }
-                self.surfaces_start = surfaces_end;
-                Some(PrimitiveBatch::Surfaces(
-                    &self.surfaces[surfaces_start..surfaces_end],
-                ))
-            }
-        }
-    }
-}
-
-#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
-pub enum PrimitiveKind {
-    Shadow,
-    #[default]
-    Quad,
-    Path,
-    Underline,
-    MonochromeSprite,
-    PolychromeSprite,
-    Surface,
-}
-
-pub enum Primitive {
-    Shadow(Shadow),
-    Quad(Quad),
-    Path(Path<ScaledPixels>),
-    Underline(Underline),
-    MonochromeSprite(MonochromeSprite),
-    PolychromeSprite(PolychromeSprite),
-    Surface(Surface),
-}
-
-impl Primitive {
-    pub fn bounds(&self) -> &Bounds<ScaledPixels> {
-        match self {
-            Primitive::Shadow(shadow) => &shadow.bounds,
-            Primitive::Quad(quad) => &quad.bounds,
-            Primitive::Path(path) => &path.bounds,
-            Primitive::Underline(underline) => &underline.bounds,
-            Primitive::MonochromeSprite(sprite) => &sprite.bounds,
-            Primitive::PolychromeSprite(sprite) => &sprite.bounds,
-            Primitive::Surface(surface) => &surface.bounds,
-        }
-    }
-
-    pub fn content_mask(&self) -> &ContentMask<ScaledPixels> {
-        match self {
-            Primitive::Shadow(shadow) => &shadow.content_mask,
-            Primitive::Quad(quad) => &quad.content_mask,
-            Primitive::Path(path) => &path.content_mask,
-            Primitive::Underline(underline) => &underline.content_mask,
-            Primitive::MonochromeSprite(sprite) => &sprite.content_mask,
-            Primitive::PolychromeSprite(sprite) => &sprite.content_mask,
-            Primitive::Surface(surface) => &surface.content_mask,
-        }
-    }
-}
-
-#[derive(Debug)]
-pub(crate) enum PrimitiveBatch<'a> {
-    Shadows(&'a [Shadow]),
-    Quads(&'a [Quad]),
-    Paths(&'a [Path<ScaledPixels>]),
-    Underlines(&'a [Underline]),
-    MonochromeSprites {
-        texture_id: AtlasTextureId,
-        sprites: &'a [MonochromeSprite],
-    },
-    PolychromeSprites {
-        texture_id: AtlasTextureId,
-        sprites: &'a [PolychromeSprite],
-    },
-    Surfaces(&'a [Surface]),
-}
-
-#[derive(Default, Debug, Clone, Eq, PartialEq)]
-#[repr(C)]
-pub struct Quad {
-    pub order: u32, // Initially a LayerId, then a DrawOrder.
-    pub bounds: Bounds<ScaledPixels>,
-    pub content_mask: ContentMask<ScaledPixels>,
-    pub background: Hsla,
-    pub border_color: Hsla,
-    pub corner_radii: Corners<ScaledPixels>,
-    pub border_widths: Edges<ScaledPixels>,
-}
-
-impl Ord for Quad {
-    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
-        self.order.cmp(&other.order)
-    }
-}
-
-impl PartialOrd for Quad {
-    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-impl From<Quad> for Primitive {
-    fn from(quad: Quad) -> Self {
-        Primitive::Quad(quad)
-    }
-}
-
-#[derive(Debug, Clone, Eq, PartialEq)]
-#[repr(C)]
-pub struct Underline {
-    pub order: u32,
-    pub bounds: Bounds<ScaledPixels>,
-    pub content_mask: ContentMask<ScaledPixels>,
-    pub thickness: ScaledPixels,
-    pub color: Hsla,
-    pub wavy: bool,
-}
-
-impl Ord for Underline {
-    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
-        self.order.cmp(&other.order)
-    }
-}
-
-impl PartialOrd for Underline {
-    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-impl From<Underline> for Primitive {
-    fn from(underline: Underline) -> Self {
-        Primitive::Underline(underline)
-    }
-}
-
-#[derive(Debug, Clone, Eq, PartialEq)]
-#[repr(C)]
-pub struct Shadow {
-    pub order: u32,
-    pub bounds: Bounds<ScaledPixels>,
-    pub corner_radii: Corners<ScaledPixels>,
-    pub content_mask: ContentMask<ScaledPixels>,
-    pub color: Hsla,
-    pub blur_radius: ScaledPixels,
-}
-
-impl Ord for Shadow {
-    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
-        self.order.cmp(&other.order)
-    }
-}
-
-impl PartialOrd for Shadow {
-    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-impl From<Shadow> for Primitive {
-    fn from(shadow: Shadow) -> Self {
-        Primitive::Shadow(shadow)
-    }
-}
-
-#[derive(Clone, Debug, Eq, PartialEq)]
-#[repr(C)]
-pub struct MonochromeSprite {
-    pub order: u32,
-    pub bounds: Bounds<ScaledPixels>,
-    pub content_mask: ContentMask<ScaledPixels>,
-    pub color: Hsla,
-    pub tile: AtlasTile,
-}
-
-impl Ord for MonochromeSprite {
-    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
-        match self.order.cmp(&other.order) {
-            std::cmp::Ordering::Equal => self.tile.tile_id.cmp(&other.tile.tile_id),
-            order => order,
-        }
-    }
-}
-
-impl PartialOrd for MonochromeSprite {
-    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-impl From<MonochromeSprite> for Primitive {
-    fn from(sprite: MonochromeSprite) -> Self {
-        Primitive::MonochromeSprite(sprite)
-    }
-}
-
-#[derive(Clone, Debug, Eq, PartialEq)]
-#[repr(C)]
-pub struct PolychromeSprite {
-    pub order: u32,
-    pub bounds: Bounds<ScaledPixels>,
-    pub content_mask: ContentMask<ScaledPixels>,
-    pub corner_radii: Corners<ScaledPixels>,
-    pub tile: AtlasTile,
-    pub grayscale: bool,
-}
-
-impl Ord for PolychromeSprite {
-    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
-        match self.order.cmp(&other.order) {
-            std::cmp::Ordering::Equal => self.tile.tile_id.cmp(&other.tile.tile_id),
-            order => order,
-        }
-    }
-}
-
-impl PartialOrd for PolychromeSprite {
-    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-impl From<PolychromeSprite> for Primitive {
-    fn from(sprite: PolychromeSprite) -> Self {
-        Primitive::PolychromeSprite(sprite)
-    }
-}
-
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct Surface {
-    pub order: u32,
-    pub bounds: Bounds<ScaledPixels>,
-    pub content_mask: ContentMask<ScaledPixels>,
-    pub image_buffer: media::core_video::CVImageBuffer,
-}
-
-impl Ord for Surface {
-    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
-        self.order.cmp(&other.order)
-    }
-}
-
-impl PartialOrd for Surface {
-    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-impl From<Surface> for Primitive {
-    fn from(surface: Surface) -> Self {
-        Primitive::Surface(surface)
-    }
-}
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
-pub(crate) struct PathId(pub(crate) usize);
-
-#[derive(Debug)]
-pub struct Path<P: Clone + Default + Debug> {
-    pub(crate) id: PathId,
-    order: u32,
-    pub(crate) bounds: Bounds<P>,
-    pub(crate) content_mask: ContentMask<P>,
-    pub(crate) vertices: Vec<PathVertex<P>>,
-    pub(crate) color: Hsla,
-    start: Point<P>,
-    current: Point<P>,
-    contour_count: usize,
-}
-
-impl Path<Pixels> {
-    pub fn new(start: Point<Pixels>) -> Self {
-        Self {
-            id: PathId(0),
-            order: 0,
-            vertices: Vec::new(),
-            start,
-            current: start,
-            bounds: Bounds {
-                origin: start,
-                size: Default::default(),
-            },
-            content_mask: Default::default(),
-            color: Default::default(),
-            contour_count: 0,
-        }
-    }
-
-    pub fn scale(&self, factor: f32) -> Path<ScaledPixels> {
-        Path {
-            id: self.id,
-            order: self.order,
-            bounds: self.bounds.scale(factor),
-            content_mask: self.content_mask.scale(factor),
-            vertices: self
-                .vertices
-                .iter()
-                .map(|vertex| vertex.scale(factor))
-                .collect(),
-            start: self.start.map(|start| start.scale(factor)),
-            current: self.current.scale(factor),
-            contour_count: self.contour_count,
-            color: self.color,
-        }
-    }
-
-    pub fn line_to(&mut self, to: Point<Pixels>) {
-        self.contour_count += 1;
-        if self.contour_count > 1 {
-            self.push_triangle(
-                (self.start, self.current, to),
-                (point(0., 1.), point(0., 1.), point(0., 1.)),
-            );
-        }
-        self.current = to;
-    }
-
-    pub fn curve_to(&mut self, to: Point<Pixels>, ctrl: Point<Pixels>) {
-        self.contour_count += 1;
-        if self.contour_count > 1 {
-            self.push_triangle(
-                (self.start, self.current, to),
-                (point(0., 1.), point(0., 1.), point(0., 1.)),
-            );
-        }
-
-        self.push_triangle(
-            (self.current, ctrl, to),
-            (point(0., 0.), point(0.5, 0.), point(1., 1.)),
-        );
-        self.current = to;
-    }
-
-    fn push_triangle(
-        &mut self,
-        xy: (Point<Pixels>, Point<Pixels>, Point<Pixels>),
-        st: (Point<f32>, Point<f32>, Point<f32>),
-    ) {
-        self.bounds = self
-            .bounds
-            .union(&Bounds {
-                origin: xy.0,
-                size: Default::default(),
-            })
-            .union(&Bounds {
-                origin: xy.1,
-                size: Default::default(),
-            })
-            .union(&Bounds {
-                origin: xy.2,
-                size: Default::default(),
-            });
-
-        self.vertices.push(PathVertex {
-            xy_position: xy.0,
-            st_position: st.0,
-            content_mask: Default::default(),
-        });
-        self.vertices.push(PathVertex {
-            xy_position: xy.1,
-            st_position: st.1,
-            content_mask: Default::default(),
-        });
-        self.vertices.push(PathVertex {
-            xy_position: xy.2,
-            st_position: st.2,
-            content_mask: Default::default(),
-        });
-    }
-}
-
-impl Eq for Path<ScaledPixels> {}
-
-impl PartialEq for Path<ScaledPixels> {
-    fn eq(&self, other: &Self) -> bool {
-        self.order == other.order
-    }
-}
-
-impl Ord for Path<ScaledPixels> {
-    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
-        self.order.cmp(&other.order)
-    }
-}
-
-impl PartialOrd for Path<ScaledPixels> {
-    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-impl From<Path<ScaledPixels>> for Primitive {
-    fn from(path: Path<ScaledPixels>) -> Self {
-        Primitive::Path(path)
-    }
-}
-
-#[derive(Clone, Debug)]
-#[repr(C)]
-pub struct PathVertex<P: Clone + Default + Debug> {
-    pub(crate) xy_position: Point<P>,
-    pub(crate) st_position: Point<f32>,
-    pub(crate) content_mask: ContentMask<P>,
-}
-
-impl PathVertex<Pixels> {
-    pub fn scale(&self, factor: f32) -> PathVertex<ScaledPixels> {
-        PathVertex {
-            xy_position: self.xy_position.scale(factor),
-            st_position: self.st_position,
-            content_mask: self.content_mask.scale(factor),
-        }
-    }
-}
-
-#[derive(Copy, Clone, Debug)]
-pub struct AtlasId(pub(crate) usize);

crates/gpui2/src/test.rs 🔗

@@ -1,80 +0,0 @@
-use crate::{Entity, Subscription, TestAppContext, TestDispatcher};
-use futures::StreamExt as _;
-use rand::prelude::*;
-use smol::channel;
-use std::{
-    env,
-    panic::{self, RefUnwindSafe},
-};
-
-pub fn run_test(
-    mut num_iterations: u64,
-    max_retries: usize,
-    test_fn: &mut (dyn RefUnwindSafe + Fn(TestDispatcher, u64)),
-    on_fail_fn: Option<fn()>,
-    _fn_name: String, // todo!("re-enable fn_name")
-) {
-    let starting_seed = env::var("SEED")
-        .map(|seed| seed.parse().expect("invalid SEED variable"))
-        .unwrap_or(0);
-    let is_randomized = num_iterations > 1;
-    if let Ok(iterations) = env::var("ITERATIONS") {
-        num_iterations = iterations.parse().expect("invalid ITERATIONS variable");
-    }
-
-    for seed in starting_seed..starting_seed + num_iterations {
-        let mut retry = 0;
-        loop {
-            if is_randomized {
-                eprintln!("seed = {seed}");
-            }
-            let result = panic::catch_unwind(|| {
-                let dispatcher = TestDispatcher::new(StdRng::seed_from_u64(seed));
-                test_fn(dispatcher, seed);
-            });
-
-            match result {
-                Ok(_) => break,
-                Err(error) => {
-                    if retry < max_retries {
-                        println!("retrying: attempt {}", retry);
-                        retry += 1;
-                    } else {
-                        if is_randomized {
-                            eprintln!("failing seed: {}", seed);
-                        }
-                        on_fail_fn.map(|f| f());
-                        panic::resume_unwind(error);
-                    }
-                }
-            }
-        }
-    }
-}
-
-pub struct Observation<T> {
-    rx: channel::Receiver<T>,
-    _subscription: Subscription,
-}
-
-impl<T: 'static> futures::Stream for Observation<T> {
-    type Item = T;
-
-    fn poll_next(
-        mut self: std::pin::Pin<&mut Self>,
-        cx: &mut std::task::Context<'_>,
-    ) -> std::task::Poll<Option<Self::Item>> {
-        self.rx.poll_next_unpin(cx)
-    }
-}
-
-pub fn observe<T: 'static>(entity: &impl Entity<T>, cx: &mut TestAppContext) -> Observation<()> {
-    let (tx, rx) = smol::channel::unbounded();
-    let _subscription = cx.update(|cx| {
-        cx.observe(entity, move |_, _| {
-            let _ = smol::block_on(tx.send(()));
-        })
-    });
-
-    Observation { rx, _subscription }
-}

crates/gpui2/src/util.rs 🔗

@@ -1,51 +0,0 @@
-#[cfg(any(test, feature = "test-support"))]
-use std::time::Duration;
-
-#[cfg(any(test, feature = "test-support"))]
-use futures::Future;
-
-#[cfg(any(test, feature = "test-support"))]
-use smol::future::FutureExt;
-
-pub use util::*;
-
-#[cfg(any(test, feature = "test-support"))]
-pub async fn timeout<F, T>(timeout: Duration, f: F) -> Result<T, ()>
-where
-    F: Future<Output = T>,
-{
-    let timer = async {
-        smol::Timer::after(timeout).await;
-        Err(())
-    };
-    let future = async move { Ok(f.await) };
-    timer.race(future).await
-}
-
-#[cfg(any(test, feature = "test-support"))]
-pub struct CwdBacktrace<'a>(pub &'a backtrace::Backtrace);
-
-#[cfg(any(test, feature = "test-support"))]
-impl<'a> std::fmt::Debug for CwdBacktrace<'a> {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        use backtrace::{BacktraceFmt, BytesOrWideString};
-
-        let cwd = std::env::current_dir().unwrap();
-        let cwd = cwd.parent().unwrap();
-        let mut print_path = |fmt: &mut std::fmt::Formatter<'_>, path: BytesOrWideString<'_>| {
-            std::fmt::Display::fmt(&path, fmt)
-        };
-        let mut fmt = BacktraceFmt::new(f, backtrace::PrintFmt::Full, &mut print_path);
-        for frame in self.0.frames() {
-            let mut formatted_frame = fmt.frame();
-            if frame
-                .symbols()
-                .iter()
-                .any(|s| s.filename().map_or(false, |f| f.starts_with(&cwd)))
-            {
-                formatted_frame.backtrace_frame(frame)?;
-            }
-        }
-        fmt.finish()
-    }
-}

crates/install_cli/Cargo.toml 🔗

@@ -14,5 +14,5 @@ test-support = []
 smol.workspace = true
 anyhow.workspace = true
 log.workspace = true
-gpui = { path = "../gpui2", package = "gpui2" }
+gpui = { path = "../gpui" }
 util = { path = "../util" }

crates/journal/Cargo.toml 🔗

@@ -10,7 +10,7 @@ doctest = false
 
 [dependencies]
 editor = { path = "../editor" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 util = { path = "../util" }
 workspace = { path = "../workspace" }
 settings = { path = "../settings" }

crates/language/Cargo.toml 🔗

@@ -26,7 +26,7 @@ clock = { path = "../clock" }
 collections = { path = "../collections" }
 fuzzy = {  path = "../fuzzy" }
 git = { path = "../git" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 lsp = { path = "../lsp" }
 rpc = { path = "../rpc" }
 settings = { path = "../settings" }
@@ -63,7 +63,7 @@ pulldown-cmark = { version = "0.9.2", default-features = false }
 [dev-dependencies]
 client = { path = "../client", features = ["test-support"] }
 collections = { path = "../collections", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 lsp = { path = "../lsp", features = ["test-support"] }
 text = { path = "../text", features = ["test-support"] }
 settings = { path = "../settings", features = ["test-support"] }

crates/language_selector/Cargo.toml 🔗

@@ -12,7 +12,7 @@ doctest = false
 editor = { path = "../editor" }
 fuzzy = {  path = "../fuzzy" }
 language = { path = "../language" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 picker = { path = "../picker" }
 project = { path = "../project" }
 theme = { path = "../theme" }

crates/language_tools/Cargo.toml 🔗

@@ -16,7 +16,7 @@ theme = { path = "../theme" }
 language = { path = "../language" }
 project = { path = "../project" }
 workspace = { path = "../workspace" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 ui = { path = "../ui" }
 util = { path = "../util" }
 lsp = { path = "../lsp" }
@@ -28,7 +28,7 @@ tree-sitter.workspace = true
 [dev-dependencies]
 client = { path = "../client", features = ["test-support"] }
 editor = { path = "../editor", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 util = { path = "../util", features = ["test-support"] }
 env_logger.workspace = true
 unindent.workspace = true

crates/live_kit_client/Cargo.toml 🔗

@@ -23,7 +23,7 @@ test-support = [
 
 [dependencies]
 collections = { path = "../collections", optional = true }
-gpui = { package = "gpui2", path = "../gpui2", optional = true }
+gpui = { path = "../gpui", optional = true }
 live_kit_server = { path = "../live_kit_server", optional = true }
 media = { path = "../media" }
 
@@ -41,7 +41,7 @@ nanoid = { version ="0.4", optional = true}
 
 [dev-dependencies]
 collections = { path = "../collections", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 live_kit_server = { path = "../live_kit_server" }
 media = { path = "../media" }
 nanoid = "0.4"

crates/lsp/Cargo.toml 🔗

@@ -13,7 +13,7 @@ test-support = ["async-pipe"]
 
 [dependencies]
 collections = { path = "../collections" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 util = { path = "../util" }
 
 anyhow.workspace = true
@@ -29,7 +29,7 @@ serde_json.workspace = true
 smol.workspace = true
 
 [dev-dependencies]
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 util = { path = "../util", features = ["test-support"] }
 
 async-pipe = { git = "https://github.com/zed-industries/async-pipe-rs", rev = "82d00a04211cf4e1236029aa03e6b6ce2a74c553" }

crates/menu/Cargo.toml 🔗

@@ -9,5 +9,5 @@ path = "src/menu.rs"
 doctest = false
 
 [dependencies]
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 serde = { workspace = true }

crates/multi_buffer/Cargo.toml 🔗

@@ -24,7 +24,7 @@ client = { path = "../client" }
 clock = { path = "../clock" }
 collections = { path = "../collections" }
 git = { path = "../git" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 language = { path = "../language" }
 lsp = { path = "../lsp" }
 rich_text = { path = "../rich_text" }
@@ -63,7 +63,7 @@ copilot = { path = "../copilot", features = ["test-support"] }
 text = { path = "../text", features = ["test-support"] }
 language = { path = "../language", features = ["test-support"] }
 lsp = { path = "../lsp", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 util = { path = "../util", features = ["test-support"] }
 project = { path = "../project", features = ["test-support"] }
 settings = { path = "../settings", features = ["test-support"] }

crates/notifications/Cargo.toml 🔗

@@ -23,7 +23,7 @@ clock = { path = "../clock" }
 collections = { path = "../collections" }
 db = { path = "../db" }
 feature_flags = { path = "../feature_flags" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 rpc = { path = "../rpc" }
 settings = { path = "../settings" }
 sum_tree = { path = "../sum_tree" }
@@ -36,7 +36,7 @@ time.workspace = true
 [dev-dependencies]
 client = { path = "../client", features = ["test-support"] }
 collections = { path = "../collections", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 rpc = { path = "../rpc", features = ["test-support"] }
 settings = { path = "../settings", features = ["test-support"] }
 util = { path = "../util", features = ["test-support"] }

crates/outline/Cargo.toml 🔗

@@ -11,7 +11,7 @@ doctest = false
 [dependencies]
 editor = { path = "../editor" }
 fuzzy = {  path = "../fuzzy" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 ui = { path = "../ui" }
 language = { path = "../language" }
 picker = { path = "../picker" }

crates/picker/Cargo.toml 🔗

@@ -11,7 +11,7 @@ doctest = false
 [dependencies]
 editor = { path = "../editor" }
 ui = { path = "../ui" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 menu = { path = "../menu" }
 settings = { path = "../settings" }
 util = { path = "../util" }
@@ -22,7 +22,7 @@ parking_lot.workspace = true
 
 [dev-dependencies]
 editor = { path = "../editor", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 serde_json.workspace = true
 ctor.workspace = true
 env_logger.workspace = true

crates/prettier/Cargo.toml 🔗

@@ -15,7 +15,7 @@ test-support = []
 client = { path = "../client" }
 collections = { path = "../collections"}
 language = { path = "../language" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 fs = { path = "../fs" }
 lsp = { path = "../lsp" }
 node_runtime = { path = "../node_runtime"}
@@ -31,5 +31,5 @@ parking_lot.workspace = true
 
 [dev-dependencies]
 language = { path = "../language", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 fs = { path = "../fs",  features = ["test-support"] }

crates/project/Cargo.toml 🔗

@@ -30,7 +30,7 @@ fs = { path = "../fs" }
 fsevent = { path = "../fsevent" }
 fuzzy = {  path = "../fuzzy" }
 git = { path = "../git" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 language = { path = "../language" }
 lsp = { path = "../lsp" }
 node_runtime = { path = "../node_runtime" }
@@ -73,7 +73,7 @@ client = { path = "../client", features = ["test-support"] }
 collections = { path = "../collections", features = ["test-support"] }
 db = { path = "../db", features = ["test-support"] }
 fs = { path = "../fs",  features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 language = { path = "../language", features = ["test-support"] }
 lsp = { path = "../lsp", features = ["test-support"] }
 settings = { path = "../settings", features = ["test-support"] }

crates/project_panel/Cargo.toml 🔗

@@ -12,7 +12,7 @@ doctest = false
 collections = { path = "../collections" }
 db = { path = "../db" }
 editor = { path = "../editor" }
-gpui = { path = "../gpui2", package = "gpui2" }
+gpui = { path = "../gpui" }
 menu = {  path = "../menu" }
 project = { path = "../project" }
 search = { path = "../search" }
@@ -36,6 +36,6 @@ unicase = "2.6"
 client = { path = "../client", features = ["test-support"] }
 language = { path = "../language", features = ["test-support"] }
 editor = { path = "../editor", features = ["test-support"] }
-gpui = { path = "../gpui2", package = "gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 workspace = { path = "../workspace", features = ["test-support"] }
 serde_json.workspace = true

crates/project_symbols/Cargo.toml 🔗

@@ -11,7 +11,7 @@ doctest = false
 [dependencies]
 editor = { path = "../editor" }
 fuzzy = {   path = "../fuzzy" }
-gpui = { package = "gpui2",  path = "../gpui2" }
+gpui = { path = "../gpui" }
 picker = { path = "../picker" }
 project = { path = "../project" }
 text = { path = "../text" }
@@ -29,7 +29,7 @@ smol.workspace = true
 futures.workspace = true
 editor = { path = "../editor", features = ["test-support"] }
 settings = { path = "../settings", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 language = { path = "../language", features = ["test-support"] }
 lsp = { path = "../lsp", features = ["test-support"] }
 project = { path = "../project", features = ["test-support"] }

crates/quick_action_bar/Cargo.toml 🔗

@@ -11,12 +11,12 @@ doctest = false
 [dependencies]
 assistant = { path = "../assistant" }
 editor = { path = "../editor" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 search = { path = "../search" }
 workspace = { path = "../workspace" }
 ui = { path = "../ui" }
 
 [dev-dependencies]
 editor = { path = "../editor", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 workspace = { path = "../workspace", features = ["test-support"] }

crates/recent_projects/Cargo.toml 🔗

@@ -11,7 +11,7 @@ doctest = false
 [dependencies]
 editor = { path = "../editor" }
 fuzzy = {  path = "../fuzzy" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 language = { path = "../language" }
 picker = { path = "../picker" }
 settings = { path = "../settings" }

crates/rich_text/Cargo.toml 🔗

@@ -16,7 +16,7 @@ test-support = [
 
 [dependencies]
 collections = { path = "../collections" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 sum_tree = { path = "../sum_tree" }
 theme = { path = "../theme" }
 language = { path = "../language" }

crates/rope/Cargo.toml 🔗

@@ -18,4 +18,4 @@ util = { path = "../util" }
 [dev-dependencies]
 rand.workspace = true
 util = { path = "../util", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"]  }
+gpui = { path = "../gpui", features = ["test-support"]  }

crates/rpc/Cargo.toml 🔗

@@ -15,7 +15,7 @@ test-support = ["collections/test-support", "gpui/test-support"]
 [dependencies]
 clock = { path = "../clock" }
 collections = { path = "../collections" }
-gpui = { package = "gpui2", path = "../gpui2", optional = true }
+gpui = { path = "../gpui", optional = true }
 util = { path = "../util" }
 anyhow.workspace = true
 async-lock = "2.4"
@@ -39,7 +39,7 @@ prost-build = "0.9"
 
 [dev-dependencies]
 collections = { path = "../collections", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 smol.workspace = true
 tempdir.workspace = true
 ctor.workspace = true

crates/search/Cargo.toml 🔗

@@ -12,7 +12,7 @@ doctest = false
 bitflags = "1"
 collections = { path = "../collections" }
 editor = { path = "../editor" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 language = { path = "../language" }
 menu = { path = "../menu" }
 project = { path = "../project" }
@@ -34,7 +34,7 @@ serde_json.workspace = true
 [dev-dependencies]
 client = { path = "../client", features = ["test-support"] }
 editor = { path = "../editor", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 
 workspace = { path = "../workspace", features = ["test-support"] }
 unindent.workspace = true

crates/semantic_index/Cargo.toml 🔗

@@ -11,7 +11,7 @@ doctest = false
 [dependencies]
 ai = { path = "../ai" }
 collections = { path = "../collections" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 language = { path = "../language" }
 project = { path = "../project" }
 workspace = { path = "../workspace" }
@@ -41,7 +41,7 @@ ndarray = { version = "0.15.0" }
 [dev-dependencies]
 ai = { path = "../ai", features = ["test-support"] }
 collections = { path = "../collections", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 language = { path = "../language", features = ["test-support"] }
 project = { path = "../project", features = ["test-support"] }
 rpc = { path = "../rpc", features = ["test-support"] }

crates/settings/Cargo.toml 🔗

@@ -13,7 +13,7 @@ test-support = ["gpui/test-support", "fs/test-support"]
 
 [dependencies]
 collections = { path = "../collections" }
-gpui = {package = "gpui2",  path = "../gpui2" }
+gpui = { path = "../gpui" }
 sqlez = { path = "../sqlez" }
 fs = { path = "../fs" }
 feature_flags = { path = "../feature_flags" }
@@ -35,7 +35,7 @@ tree-sitter.workspace = true
 tree-sitter-json = "*"
 
 [dev-dependencies]
-gpui = {package = "gpui2",  path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 fs = { path = "../fs", features = ["test-support"] }
 indoc.workspace = true
 pretty_assertions.workspace = true

crates/story/Cargo.toml 🔗

@@ -7,6 +7,6 @@ publish = false
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 smallvec.workspace = true
 itertools = {package = "itertools", version = "0.10"}

crates/storybook/Cargo.toml 🔗

@@ -18,7 +18,7 @@ strum = { version = "0.25.0", features = ["derive"] }
 dialoguer = { version = "0.11.0", features = ["fuzzy-select"] }
 editor = { path = "../editor" }
 fuzzy = {  path = "../fuzzy" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 indoc.workspace = true
 itertools = "0.11.0"
 language = { path = "../language" }
@@ -35,4 +35,4 @@ util = { path = "../util" }
 picker = { path = "../picker" }
 
 [dev-dependencies]
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }

crates/terminal/Cargo.toml 🔗

@@ -10,7 +10,7 @@ doctest = false
 
 
 [dependencies]
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 settings = { path = "../settings" }
 db = { path = "../db" }
 theme = { path = "../theme" }

crates/terminal_view/Cargo.toml 🔗

@@ -11,7 +11,7 @@ doctest = false
 [dependencies]
 editor = { path = "../editor" }
 language = { path = "../language" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 project = { path = "../project" }
 # search = { path = "../search" }
 settings = { path = "../settings" }
@@ -39,7 +39,7 @@ serde_derive.workspace = true
 
 [dev-dependencies]
 editor = { path = "../editor", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 client = { path = "../client", features = ["test-support"]}
 project = { path = "../project", features = ["test-support"]}
 workspace = { path = "../workspace", features = ["test-support"] }

crates/text/Cargo.toml 🔗

@@ -30,7 +30,7 @@ regex.workspace = true
 
 [dev-dependencies]
 collections = { path = "../collections", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 util = { path = "../util", features = ["test-support"] }
 ctor.workspace = true
 env_logger.workspace = true

crates/theme/Cargo.toml 🔗

@@ -21,7 +21,7 @@ doctest = false
 [dependencies]
 anyhow.workspace = true
 fs = { path = "../fs" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 indexmap = "1.6.2"
 parking_lot.workspace = true
 refineable.workspace = true
@@ -37,6 +37,6 @@ util = { path = "../util" }
 itertools = { version = "0.11.0", optional = true }
 
 [dev-dependencies]
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 fs = { path = "../fs", features = ["test-support"] }
 settings = { path = "../settings", features = ["test-support"] }

crates/theme_importer/Cargo.toml 🔗

@@ -9,7 +9,7 @@ any_ascii = "0.3.2"
 anyhow.workspace = true
 clap = { version = "4.4", features = ["derive"] }
 convert_case = "0.6.0"
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 indexmap = { version = "1.6.2", features = ["serde"] }
 json_comments = "0.2.2"
 log.workspace = true

crates/theme_selector/Cargo.toml 🔗

@@ -14,7 +14,7 @@ editor = { path = "../editor" }
 feature_flags = { path = "../feature_flags" }
 fs = { path = "../fs" }
 fuzzy = {  path = "../fuzzy" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 picker = { path = "../picker" }
 settings = { path = "../settings" }
 theme = { path = "../theme" }

crates/ui/Cargo.toml 🔗

@@ -11,7 +11,7 @@ path = "src/ui.rs"
 [dependencies]
 anyhow.workspace = true
 chrono = "0.4"
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 itertools = { version = "0.11.0", optional = true }
 menu = { path = "../menu"}
 serde.workspace = true

crates/util/Cargo.toml 🔗

@@ -11,12 +11,6 @@ doctest = true
 [features]
 test-support = ["tempdir", "git2"]
 
-# Suppress a panic when both GPUI1 and GPUI2 are loaded.
-#
-# This is used in the `theme_importer` where we need to depend on both
-# GPUI1 and GPUI2 in order to convert Zed1 themes to Zed2 themes.
-allow-multiple-gpui-versions = []
-
 [dependencies]
 anyhow.workspace = true
 backtrace = "0.3"

crates/util/src/util.rs 🔗

@@ -16,9 +16,6 @@ use std::{
     task::{Context, Poll},
 };
 
-#[cfg(not(feature = "allow-multiple-gpui-versions"))]
-use std::sync::atomic::AtomicU32;
-
 pub use backtrace::Backtrace;
 use futures::Future;
 use rand::{seq::SliceRandom, Rng};
@@ -436,23 +433,6 @@ impl<T: Ord + Clone> RangeExt<T> for RangeInclusive<T> {
     }
 }
 
-#[cfg(not(feature = "allow-multiple-gpui-versions"))]
-static GPUI_LOADED: AtomicU32 = AtomicU32::new(0);
-
-pub fn gpui2_loaded() {
-    #[cfg(not(feature = "allow-multiple-gpui-versions"))]
-    if GPUI_LOADED.fetch_add(2, std::sync::atomic::Ordering::SeqCst) != 0 {
-        panic!("=========\nYou are loading both GPUI1 and GPUI2 in the same build!\nFix Your Dependencies with cargo tree!\n=========")
-    }
-}
-
-pub fn gpui1_loaded() {
-    #[cfg(not(feature = "allow-multiple-gpui-versions"))]
-    if GPUI_LOADED.fetch_add(1, std::sync::atomic::Ordering::SeqCst) != 0 {
-        panic!("=========\nYou are loading both GPUI1 and GPUI2 in the same build!\nFix Your Dependencies with cargo tree!\n=========")
-    }
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;

crates/vcs_menu/Cargo.toml 🔗

@@ -8,7 +8,7 @@ publish = false
 [dependencies]
 fuzzy = { path = "../fuzzy"}
 fs = {path = "../fs"}
-gpui = {package = "gpui2", path = "../gpui2"}
+gpui = {path = "../gpui"}
 picker = {path = "../picker"}
 util = {path = "../util"}
 ui = {path = "../ui"}

crates/vim/Cargo.toml 🔗

@@ -27,7 +27,7 @@ serde_json.workspace = true
 collections = { path = "../collections" }
 command_palette = { path = "../command_palette" }
 editor = { path = "../editor" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 language = { path = "../language" }
 search = { path = "../search" }
 settings = { path = "../settings" }
@@ -43,7 +43,7 @@ parking_lot.workspace = true
 futures.workspace = true
 
 editor = { path = "../editor", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 language = { path = "../language", features = ["test-support"] }
 project = { path = "../project", features = ["test-support"] }
 util = { path = "../util", features = ["test-support"] }

crates/welcome/Cargo.toml 🔗

@@ -15,7 +15,7 @@ client = { path = "../client" }
 editor = { path = "../editor" }
 fs = { path = "../fs" }
 fuzzy = {  path = "../fuzzy" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 ui = { path = "../ui" }
 db = { path = "../db" }
 install_cli = { path = "../install_cli" }

crates/workspace/Cargo.toml 🔗

@@ -25,7 +25,7 @@ client = { path = "../client" }
 collections = { path = "../collections" }
 # context_menu = { path = "../context_menu" }
 fs = { path = "../fs" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 install_cli = { path = "../install_cli" }
 language = { path = "../language" }
 #menu = { path = "../menu" }
@@ -56,7 +56,7 @@ uuid.workspace = true
 [dev-dependencies]
 call = { path = "../call", features = ["test-support"] }
 client = { path = "../client", features = ["test-support"] }
-gpui = { path = "../gpui2", package = "gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 project = { path = "../project", features = ["test-support"] }
 settings = { path = "../settings", features = ["test-support"] }
 fs = { path = "../fs", features = ["test-support"] }

crates/zed/Cargo.toml 🔗

@@ -40,7 +40,7 @@ search = { path = "../search" }
 fs = { path = "../fs" }
 fsevent = { path = "../fsevent" }
 go_to_line = { path = "../go_to_line" }
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 install_cli = { path = "../install_cli" }
 journal = { path = "../journal" }
 language = { path = "../language" }
@@ -148,7 +148,7 @@ call = { path = "../call", features = ["test-support"] }
 # client = { path = "../client", features = ["test-support"] }
 # editor = { path = "../editor", features = ["test-support"] }
 # gpui = { path = "../gpui", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 language = { path = "../language", features = ["test-support"] }
 # lsp = { path = "../lsp", features = ["test-support"] }
 project = { path = "../project", features = ["test-support"] }

crates/zed_actions/Cargo.toml 🔗

@@ -7,5 +7,5 @@ publish = false
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-gpui = { package = "gpui2", path = "../gpui2" }
+gpui = { path = "../gpui" }
 serde.workspace = true

docs/src/telemetry.md 🔗

@@ -124,7 +124,7 @@ The following data is sent:
     - `operation`: The app operation that was performed
         - `first open`
         - `open`
-        - `close (only in GPUI2-powered Zed)`
+        - `close`
     - `milliseconds_since_first_event`: Same as above
 
 You can audit the metrics data that Zed has reported by running the command `zed: open telemetry log` from the command palette, or clicking `Help > View Telemetry Log` in the application menu.